ER 0.6.0 Release Version

ER 0.6.0 Release Version
This commit is contained in:
AmazingAmpharos 2018-03-03 12:50:51 -06:00 committed by GitHub
commit 67fe623f00
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 2565 additions and 884 deletions

View File

@ -29,6 +29,8 @@ def main():
Select the rate at which the heart beep sound is played at Select the rate at which the heart beep sound is played at
low health. (default: %(default)s) low health. (default: %(default)s)
''') ''')
parser.add_argument('--heartcolor', default='red', const='red', nargs='?', choices=['red', 'blue', 'green', 'yellow'],
help='Select the color of Link\'s heart meter. (default: %(default)s)')
parser.add_argument('--sprite', help='''\ parser.add_argument('--sprite', help='''\
Path to a sprite sheet to use for Link. Needs to be in Path to a sprite sheet to use for Link. Needs to be in
binary format and have a length of 0x7000 (28672) bytes, binary format and have a length of 0x7000 (28672) bytes,

View File

@ -26,7 +26,7 @@ def adjust(args):
else: else:
raise RuntimeError('Provided Rom is not a valid Link to the Past Randomizer Rom. Please provide one for adjusting.') raise RuntimeError('Provided Rom is not a valid Link to the Past Randomizer Rom. Please provide one for adjusting.')
apply_rom_settings(rom, args.heartbeep, args.quickswap, args.fastmenu, args.disablemusic, sprite) apply_rom_settings(rom, args.heartbeep, args.heartcolor, args.quickswap, args.fastmenu, args.disablemusic, sprite)
rom.write_to_file(output_path('%s.sfc' % outfilebase)) rom.write_to_file(output_path('%s.sfc' % outfilebase))

View File

@ -1,4 +1,5 @@
import copy import copy
from enum import Enum, unique
import logging import logging
import json import json
from collections import OrderedDict from collections import OrderedDict
@ -6,7 +7,7 @@ from collections import OrderedDict
class World(object): class World(object):
def __init__(self, shuffle, logic, mode, difficulty, timer, progressive, goal, algorithm, place_dungeon_items, check_beatable_only, shuffle_ganon, quickswap, fastmenu, disable_music, keysanity): def __init__(self, shuffle, logic, mode, difficulty, timer, progressive, goal, algorithm, place_dungeon_items, check_beatable_only, shuffle_ganon, quickswap, fastmenu, disable_music, keysanity, custom, customitemarray):
self.shuffle = shuffle self.shuffle = shuffle
self.logic = logic self.logic = logic
self.mode = mode self.mode = mode
@ -30,13 +31,16 @@ class World(object):
self.place_dungeon_items = place_dungeon_items # configurable in future self.place_dungeon_items = place_dungeon_items # configurable in future
self.shuffle_bonk_prizes = False self.shuffle_bonk_prizes = False
self.swamp_patch_required = False self.swamp_patch_required = False
self.powder_patch_required = False
self.ganon_at_pyramid = True self.ganon_at_pyramid = True
self.ganonstower_vanilla = True
self.sewer_light_cone = mode == 'standard' self.sewer_light_cone = mode == 'standard'
self.light_world_light_cone = False self.light_world_light_cone = False
self.dark_world_light_cone = False self.dark_world_light_cone = False
self.treasure_hunt_count = 0 self.treasure_hunt_count = 0
self.treasure_hunt_icon = 'Triforce Piece' self.treasure_hunt_icon = 'Triforce Piece'
self.clock_mode = 'off' self.clock_mode = 'off'
self.rupoor_cost = 10
self.aga_randomness = True self.aga_randomness = True
self.lock_aga_door_in_escape = False self.lock_aga_door_in_escape = False
self.fix_trock_doors = self.shuffle != 'vanilla' self.fix_trock_doors = self.shuffle != 'vanilla'
@ -52,9 +56,13 @@ class World(object):
self.fastmenu = fastmenu self.fastmenu = fastmenu
self.disable_music = disable_music self.disable_music = disable_music
self.keysanity = keysanity self.keysanity = keysanity
self.custom = custom
self.customitemarray = customitemarray
self.can_take_damage = True self.can_take_damage = True
self.difficulty_requirements = None self.difficulty_requirements = None
self.fix_fake_world = True
self.spoiler = Spoiler(self) self.spoiler = Spoiler(self)
self.lamps_needed_for_dark_rooms = 1
def intialize_regions(self): def intialize_regions(self):
for region in self.regions: for region in self.regions:
@ -247,19 +255,31 @@ class World(object):
@property @property
def option_identifier(self): def option_identifier(self):
logic = 0 if self.logic == 'noglitches' else 1 id_value = 0
mode = ['standard', 'open', 'swordless'].index(self.mode) id_value_max = 1
dungeonitems = 0 if self.place_dungeon_items else 1
goal = ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'].index(self.goal) def markbool(value):
shuffle = ['vanilla', 'simple', 'restricted', 'full', 'madness', 'insanity', 'dungeonsfull', 'dungeonssimple'].index(self.shuffle) nonlocal id_value, id_value_max
difficulty = ['easy', 'normal', 'hard', 'expert', 'insane'].index(self.difficulty) id_value += id_value_max * bool(value)
timer = ['none', 'display', 'timed', 'timed-ohko', 'timed-countdown', 'ohko'].index(self.timer) id_value_max *= 2
progressive = ['on', 'off', 'random'].index(self.progressive) def marksequence(options, value):
algorithm = ['freshness', 'flood', 'vt21', 'vt22', 'vt25', 'vt26', 'balanced'].index(self.algorithm) nonlocal id_value, id_value_max
beatableonly = 1 if self.check_beatable_only else 0 id_value += id_value_max * options.index(value)
shuffleganon = 1 if self.shuffle_ganon else 0 id_value_max *= len(options)
keysanity = 1 if self.keysanity else 0 markbool(self.logic == 'noglitches')
return logic | (beatableonly << 1) | (dungeonitems << 2) | (shuffleganon << 3) | (goal << 4) | (shuffle << 7) | (difficulty << 11) | (algorithm << 13) | (mode << 16) | (keysanity << 18) | (timer << 19) | (progressive << 21) marksequence(['standard', 'open', 'swordless'], self.mode)
markbool(self.place_dungeon_items)
marksequence(['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'], self.goal)
marksequence(['vanilla', 'simple', 'restricted', 'full', 'crossed', 'insanity', 'restricted_legacy', 'full_legacy', 'madness_legacy', 'insanity_legacy', 'dungeonsfull', 'dungeonssimple'], self.shuffle)
marksequence(['easy', 'normal', 'hard', 'expert', 'insane'], self.difficulty)
marksequence(['none', 'display', 'timed', 'timed-ohko', 'timed-countdown', 'ohko'], self.timer)
marksequence(['on', 'off', 'random'], self.progressive)
marksequence(['freshness', 'flood', 'vt21', 'vt22', 'vt25', 'vt26', 'balanced'], self.algorithm)
markbool(self.check_beatable_only)
markbool(self.shuffle_ganon)
markbool(self.keysanity)
assert id_value_max <= 0xFFFFFFFF
return id_value
class CollectionState(object): class CollectionState(object):
@ -383,17 +403,17 @@ class CollectionState(object):
def can_lift_heavy_rocks(self): def can_lift_heavy_rocks(self):
return self.has('Titans Mitts') return self.has('Titans Mitts')
def can_extend_magic(self, smallmagic=8): #This reflects the total magic Link has, not the total extra he has. def can_extend_magic(self, smallmagic=8, fullrefill=False): #This reflects the total magic Link has, not the total extra he has.
basemagic = 8 basemagic = 8
if self.has('Quarter Magic'): if self.has('Quarter Magic'):
basemagic = 32 basemagic = 32
elif self.has('Half Magic'): elif self.has('Half Magic'):
basemagic = 16 basemagic = 16
if self.world.difficulty == 'hard': if self.world.difficulty == 'hard' and not fullrefill:
basemagic = basemagic + int(basemagic * 0.5 * self.bottle_count()) basemagic = basemagic + int(basemagic * 0.5 * self.bottle_count())
elif self.world.difficulty == 'expert': elif self.world.difficulty == 'expert' and not fullrefill:
basemagic = basemagic + int(basemagic * 0.25 * self.bottle_count()) basemagic = basemagic + int(basemagic * 0.25 * self.bottle_count())
elif self.world.difficulty == 'insane': elif self.world.difficulty == 'insane' and not fullrefill:
basemagic = basemagic basemagic = basemagic
else: else:
basemagic = basemagic + basemagic * self.bottle_count() basemagic = basemagic + basemagic * self.bottle_count()
@ -532,17 +552,32 @@ class CollectionState(object):
raise RuntimeError('Cannot parse %s.' % item) raise RuntimeError('Cannot parse %s.' % item)
@unique
class RegionType(Enum):
LightWorld = 1
DarkWorld = 2
Cave = 3 # Also includes Houses
Dungeon = 4
@property
def is_indoors(self):
"""Shorthand for checking if Cave or Dungeon"""
return self in (RegionType.Cave, RegionType.Dungeon)
class Region(object): class Region(object):
def __init__(self, name): def __init__(self, name, type):
self.name = name self.name = name
self.type = type
self.entrances = [] self.entrances = []
self.exits = [] self.exits = []
self.locations = [] self.locations = []
self.dungeon = None self.dungeon = None
self.world = None self.world = None
self.is_light_world = False # will be set aftermaking connections. self.is_light_world = False # will be set aftermaking connections.
self.is_dark_world = False
self.spot_type = 'Region' self.spot_type = 'Region'
self.hint_text = 'Hyrule' self.hint_text = 'Hyrule'
self.recursion_count = 0 self.recursion_count = 0

View File

@ -12,9 +12,9 @@ def create_dungeons(world):
world.get_region(region).dungeon = dungeon world.get_region(region).dungeon = dungeon
return dungeon return dungeon
ES = make_dungeon('Hyrule Castle', ['Hyrule Castle', 'Sewers', 'Sewers (Dark)', 'Sanctuary'], None, [ItemFactory('Small Key (Escape)')], [ItemFactory('Map (Escape)')]) ES = make_dungeon('Hyrule Castle', ['Hyrule Castle', 'Sewers', 'Sewer Drop', 'Sewers (Dark)', 'Sanctuary'], None, [ItemFactory('Small Key (Escape)')], [ItemFactory('Map (Escape)')])
EP = make_dungeon('Eastern Palace', ['Eastern Palace'], ItemFactory('Big Key (Eastern Palace)'), [], ItemFactory(['Map (Eastern Palace)', 'Compass (Eastern Palace)'])) EP = make_dungeon('Eastern Palace', ['Eastern Palace'], ItemFactory('Big Key (Eastern Palace)'), [], ItemFactory(['Map (Eastern Palace)', 'Compass (Eastern Palace)']))
DP = make_dungeon('Desert Palace', ['Desert Palace North', 'Desert Palace Main', 'Desert Palace East'], ItemFactory('Big Key (Desert Palace)'), [ItemFactory('Small Key (Desert Palace)')], ItemFactory(['Map (Desert Palace)', 'Compass (Desert Palace)'])) DP = make_dungeon('Desert Palace', ['Desert Palace North', 'Desert Palace Main (Inner)', 'Desert Palace Main (Outer)', 'Desert Palace East'], ItemFactory('Big Key (Desert Palace)'), [ItemFactory('Small Key (Desert Palace)')], ItemFactory(['Map (Desert Palace)', 'Compass (Desert Palace)']))
ToH = make_dungeon('Tower of Hera', ['Tower of Hera (Bottom)', 'Tower of Hera (Basement)', 'Tower of Hera (Top)'], ItemFactory('Big Key (Tower of Hera)'), [ItemFactory('Small Key (Tower of Hera)')], ItemFactory(['Map (Tower of Hera)', 'Compass (Tower of Hera)'])) ToH = make_dungeon('Tower of Hera', ['Tower of Hera (Bottom)', 'Tower of Hera (Basement)', 'Tower of Hera (Top)'], ItemFactory('Big Key (Tower of Hera)'), [ItemFactory('Small Key (Tower of Hera)')], ItemFactory(['Map (Tower of Hera)', 'Compass (Tower of Hera)']))
AT = make_dungeon('Agahnims Tower', ['Agahnims Tower', 'Agahnim 1'], None, ItemFactory(['Small Key (Agahnims Tower)'] * 2), []) AT = make_dungeon('Agahnims Tower', ['Agahnims Tower', 'Agahnim 1'], None, ItemFactory(['Small Key (Agahnims Tower)'] * 2), [])
PoD = make_dungeon('Palace of Darkness', ['Palace of Darkness (Entrance)', 'Palace of Darkness (Center)', 'Palace of Darkness (Big Key Chest)', 'Palace of Darkness (Bonk Section)', 'Palace of Darkness (North)', 'Palace of Darkness (Maze)', 'Palace of Darkness (Harmless Hellway)', 'Palace of Darkness (Final Section)'], ItemFactory('Big Key (Palace of Darkness)'), ItemFactory(['Small Key (Palace of Darkness)'] * 6), ItemFactory(['Map (Palace of Darkness)', 'Compass (Palace of Darkness)'])) PoD = make_dungeon('Palace of Darkness', ['Palace of Darkness (Entrance)', 'Palace of Darkness (Center)', 'Palace of Darkness (Big Key Chest)', 'Palace of Darkness (Bonk Section)', 'Palace of Darkness (North)', 'Palace of Darkness (Maze)', 'Palace of Darkness (Harmless Hellway)', 'Palace of Darkness (Final Section)'], ItemFactory('Big Key (Palace of Darkness)'), ItemFactory(['Small Key (Palace of Darkness)'] * 6), ItemFactory(['Map (Palace of Darkness)', 'Compass (Palace of Darkness)']))

View File

@ -120,25 +120,27 @@ def start():
slightly biased to placing progression items with slightly biased to placing progression items with
less restrictions. less restrictions.
''') ''')
parser.add_argument('--shuffle', default='full', const='full', nargs='?', choices=['vanilla', 'simple', 'restricted', 'full', 'madness', 'insanity', 'dungeonsfull', 'dungeonssimple'], parser.add_argument('--shuffle', default='full', const='full', nargs='?', choices=['vanilla', 'simple', 'restricted', 'full', 'crossed', 'insanity', 'restricted_legacy', 'full_legacy', 'madness_legacy', 'insanity_legacy', 'dungeonsfull', 'dungeonssimple'],
help='''\ help='''\
Select Entrance Shuffling Algorithm. (default: %(default)s) Select Entrance Shuffling Algorithm. (default: %(default)s)
Full: Mix cave and dungeon entrances freely. Full: Mix cave and dungeon entrances freely while limiting
multi-entrance caves to one world.
Simple: Shuffle Dungeon Entrances/Exits between each other Simple: Shuffle Dungeon Entrances/Exits between each other
and keep all 4-entrance dungeons confined to one and keep all 4-entrance dungeons confined to one
location. All caves outside of death mountain are location. All caves outside of death mountain are
shuffled in pairs. shuffled in pairs and matched by original type.
Restricted: Use Dungeons shuffling from Simple but freely Restricted: Use Dungeons shuffling from Simple but freely
connect remaining entrances. connect remaining entrances.
Madness: Decouple entrances and exits from each other and Crossed: Mix cave and dungeon entrances freely while allowing
shuffle them freely, only ensuring that no fake caves to cross between worlds.
Light/Dark World happens and all locations are Insanity: Decouple entrances and exits from each other and
reachable. shuffle them freely. Caves that used to be single
Insanity: Madness without the world restrictions. Mirror and entrance will still exit to the same location from
Pearl are provided early to ensure Filling algorithm which they are entered.
works properly. Deal with Fake LW/DW at your Vanilla: All entrances are in the same locations they were
discretion. in the base game.
Experimental. Legacy shuffles preserve behavior from older versions of the
entrance randomizer including significant technical limitations.
The dungeon variants only mix up dungeons and keep the rest of The dungeon variants only mix up dungeons and keep the rest of
the overworld vanilla. the overworld vanilla.
''') ''')
@ -163,6 +165,8 @@ def start():
Keys (and other dungeon items) are no longer restricted to Keys (and other dungeon items) are no longer restricted to
their dungeons, but can be anywhere their dungeons, but can be anywhere
''', action='store_true') ''', action='store_true')
parser.add_argument('--custom', default=False, help='Not supported.')
parser.add_argument('--customitemarray', default=False, help='Not supported.')
parser.add_argument('--nodungeonitems', help='''\ parser.add_argument('--nodungeonitems', help='''\
Remove Maps and Compasses from Itempool, replacing them by Remove Maps and Compasses from Itempool, replacing them by
empty slots. empty slots.
@ -172,15 +176,19 @@ def start():
ensure all locations are reachable. This only has an effect ensure all locations are reachable. This only has an effect
on the restrictive algorithm currently. on the restrictive algorithm currently.
''', action='store_true') ''', action='store_true')
parser.add_argument('--shuffleganon', help='''\ # included for backwards compatibility
If set, include the Pyramid Hole and Ganon's Tower in the parser.add_argument('--shuffleganon', help=argparse.SUPPRESS, action='store_true', default=True)
entrance shuffle pool. parser.add_argument('--no-shuffleganon', help='''\
''', action='store_true') If set, the Pyramid Hole and Ganon's Tower are not
included entrance shuffle pool.
''', action='store_false', dest='shuffleganon')
parser.add_argument('--heartbeep', default='normal', const='normal', nargs='?', choices=['normal', 'half', 'quarter', 'off'], parser.add_argument('--heartbeep', default='normal', const='normal', nargs='?', choices=['normal', 'half', 'quarter', 'off'],
help='''\ help='''\
Select the rate at which the heart beep sound is played at Select the rate at which the heart beep sound is played at
low health. (default: %(default)s) low health. (default: %(default)s)
''') ''')
parser.add_argument('--heartcolor', default='red', const='red', nargs='?', choices=['red', 'blue', 'green', 'yellow'],
help='Select the color of Link\'s heart meter. (default: %(default)s)')
parser.add_argument('--sprite', help='''\ parser.add_argument('--sprite', help='''\
Path to a sprite sheet to use for Link. Needs to be in Path to a sprite sheet to use for Link. Needs to be in
binary format and have a length of 0x7000 (28672) bytes, binary format and have a length of 0x7000 (28672) bytes,

File diff suppressed because it is too large Load Diff

16
Fill.py
View File

@ -1,6 +1,8 @@
import random import random
import logging import logging
class FillError(RuntimeError):
pass
def distribute_items_cutoff(world, cutoffrate=0.33): def distribute_items_cutoff(world, cutoffrate=0.33):
# get list of locations to fill in # get list of locations to fill in
@ -53,7 +55,7 @@ def distribute_items_cutoff(world, cutoffrate=0.33):
logging.getLogger('').warning('Not all locations reachable. Game beatable anyway.') logging.getLogger('').warning('Not all locations reachable. Game beatable anyway.')
progress_done = True progress_done = True
continue continue
raise RuntimeError('No more progress items left to place.') raise FillError('No more progress items left to place.')
spot_to_fill = None spot_to_fill = None
for location in fill_locations if placed_advancement_items / total_advancement_items < cutoffrate else reversed(fill_locations): for location in fill_locations if placed_advancement_items / total_advancement_items < cutoffrate else reversed(fill_locations):
@ -66,7 +68,7 @@ def distribute_items_cutoff(world, cutoffrate=0.33):
if world.can_beat_game(): if world.can_beat_game():
logging.getLogger('').warning('Not all items placed. Game beatable anyway.') logging.getLogger('').warning('Not all items placed. Game beatable anyway.')
break break
raise RuntimeError('No more spots to place %s' % item_to_place) raise FillError('No more spots to place %s' % item_to_place)
world.push_item(spot_to_fill, item_to_place, True) world.push_item(spot_to_fill, item_to_place, True)
itempool.remove(item_to_place) itempool.remove(item_to_place)
@ -121,7 +123,7 @@ def distribute_items_staleness(world):
logging.getLogger('').warning('Not all locations reachable. Game beatable anyway.') logging.getLogger('').warning('Not all locations reachable. Game beatable anyway.')
progress_done = True progress_done = True
continue continue
raise RuntimeError('No more progress items left to place.') raise FillError('No more progress items left to place.')
spot_to_fill = None spot_to_fill = None
for location in fill_locations: for location in fill_locations:
@ -147,7 +149,7 @@ def distribute_items_staleness(world):
if world.can_beat_game(): if world.can_beat_game():
logging.getLogger('').warning('Not all items placed. Game beatable anyway.') logging.getLogger('').warning('Not all items placed. Game beatable anyway.')
break break
raise RuntimeError('No more spots to place %s' % item_to_place) raise FillError('No more spots to place %s' % item_to_place)
world.push_item(spot_to_fill, item_to_place, True) world.push_item(spot_to_fill, item_to_place, True)
itempool.remove(item_to_place) itempool.remove(item_to_place)
@ -185,7 +187,7 @@ def fill_restrictive(world, base_state, locations, itempool):
if not world.check_beatable_only: if not world.check_beatable_only:
logging.getLogger('').warning('Not all items placed. Game beatable anyway.') logging.getLogger('').warning('Not all items placed. Game beatable anyway.')
break break
raise RuntimeError('No more spots to place %s' % item_to_place) raise FillError('No more spots to place %s' % item_to_place)
world.push_item(spot_to_fill, item_to_place, False) world.push_item(spot_to_fill, item_to_place, False)
locations.remove(spot_to_fill) locations.remove(spot_to_fill)
@ -205,7 +207,7 @@ def distribute_items_restrictive(world, gftower_trash_count=0, fill_locations=No
restitempool = [item for item in world.itempool if not item.advancement and not item.priority] restitempool = [item for item in world.itempool if not item.advancement and not item.priority]
# fill in gtower locations with trash first # fill in gtower locations with trash first
if not world.shuffle_ganon: if world.ganonstower_vanilla:
gtower_locations = [location for location in fill_locations if 'Ganons Tower' in location.name] gtower_locations = [location for location in fill_locations if 'Ganons Tower' in location.name]
random.shuffle(gtower_locations) random.shuffle(gtower_locations)
trashcnt = 0 trashcnt = 0
@ -281,7 +283,7 @@ def flood_items(world):
if candidate_item_to_place is not None: if candidate_item_to_place is not None:
item_to_place = candidate_item_to_place item_to_place = candidate_item_to_place
else: else:
raise RuntimeError('No more progress items left to place.') raise FillError('No more progress items left to place.')
# find item to replace with progress item # find item to replace with progress item
location_list = world.get_reachable_locations() location_list = world.get_reachable_locations()

605
Gui.py
View File

@ -25,8 +25,10 @@ def guiMain(args=None):
notebook = ttk.Notebook(mainWindow) notebook = ttk.Notebook(mainWindow)
randomizerWindow = ttk.Frame(notebook) randomizerWindow = ttk.Frame(notebook)
adjustWindow = ttk.Frame(notebook) adjustWindow = ttk.Frame(notebook)
customWindow = ttk.Frame(notebook)
notebook.add(randomizerWindow, text='Randomize') notebook.add(randomizerWindow, text='Randomize')
notebook.add(adjustWindow, text='Adjust') notebook.add(adjustWindow, text='Adjust')
notebook.add(customWindow, text='Custom')
notebook.pack() notebook.pack()
# Shared Controls # Shared Controls
@ -67,7 +69,10 @@ def guiMain(args=None):
disableMusicVar = IntVar() disableMusicVar = IntVar()
disableMusicCheckbutton = Checkbutton(checkBoxFrame, text="Disable game music", variable=disableMusicVar) disableMusicCheckbutton = Checkbutton(checkBoxFrame, text="Disable game music", variable=disableMusicVar)
shuffleGanonVar = IntVar() shuffleGanonVar = IntVar()
shuffleGanonVar.set(1) #set default
shuffleGanonCheckbutton = Checkbutton(checkBoxFrame, text="Include Ganon's Tower and Pyramid Hole in shuffle pool", variable=shuffleGanonVar) shuffleGanonCheckbutton = Checkbutton(checkBoxFrame, text="Include Ganon's Tower and Pyramid Hole in shuffle pool", variable=shuffleGanonVar)
customVar = IntVar()
customCheckbutton = Checkbutton(checkBoxFrame, text="Use custom item pool", variable=customVar)
createSpoilerCheckbutton.pack(expand=True, anchor=W) createSpoilerCheckbutton.pack(expand=True, anchor=W)
suppressRomCheckbutton.pack(expand=True, anchor=W) suppressRomCheckbutton.pack(expand=True, anchor=W)
@ -77,6 +82,7 @@ def guiMain(args=None):
beatableOnlyCheckbutton.pack(expand=True, anchor=W) beatableOnlyCheckbutton.pack(expand=True, anchor=W)
disableMusicCheckbutton.pack(expand=True, anchor=W) disableMusicCheckbutton.pack(expand=True, anchor=W)
shuffleGanonCheckbutton.pack(expand=True, anchor=W) shuffleGanonCheckbutton.pack(expand=True, anchor=W)
customCheckbutton.pack(expand=True, anchor=W)
fileDialogFrame = Frame(rightHalfFrame) fileDialogFrame = Frame(rightHalfFrame)
@ -188,7 +194,7 @@ def guiMain(args=None):
shuffleFrame = Frame(drowDownFrame) shuffleFrame = Frame(drowDownFrame)
shuffleVar = StringVar() shuffleVar = StringVar()
shuffleVar.set('full') shuffleVar.set('full')
shuffleOptionMenu = OptionMenu(shuffleFrame, shuffleVar, 'vanilla', 'simple', 'restricted', 'full', 'madness', 'insanity', 'dungeonsfull', 'dungeonssimple') shuffleOptionMenu = OptionMenu(shuffleFrame, shuffleVar, 'vanilla', 'simple', 'restricted', 'full', 'crossed', 'insanity', 'restricted_legacy', 'full_legacy', 'madness_legacy', 'insanity_legacy', 'dungeonsfull', 'dungeonssimple')
shuffleOptionMenu.pack(side=RIGHT) shuffleOptionMenu.pack(side=RIGHT)
shuffleLabel = Label(shuffleFrame, text='Entrance shuffle algorithm') shuffleLabel = Label(shuffleFrame, text='Entrance shuffle algorithm')
shuffleLabel.pack(side=LEFT) shuffleLabel.pack(side=LEFT)
@ -201,6 +207,13 @@ def guiMain(args=None):
heartbeepLabel = Label(heartbeepFrame, text='Heartbeep sound rate') heartbeepLabel = Label(heartbeepFrame, text='Heartbeep sound rate')
heartbeepLabel.pack(side=LEFT) heartbeepLabel.pack(side=LEFT)
heartcolorFrame = Frame(drowDownFrame)
heartcolorVar = StringVar()
heartcolorVar.set('red')
heartcolorOptionMenu = OptionMenu(heartcolorFrame, heartcolorVar, 'red', 'blue', 'green', 'yellow')
heartcolorOptionMenu.pack(side=RIGHT)
heartcolorLabel = Label(heartcolorFrame, text='Heart color')
heartcolorLabel.pack(side=LEFT)
fastMenuFrame = Frame(drowDownFrame) fastMenuFrame = Frame(drowDownFrame)
fastMenuVar = StringVar() fastMenuVar = StringVar()
@ -219,6 +232,7 @@ def guiMain(args=None):
algorithmFrame.pack(expand=True, anchor=E) algorithmFrame.pack(expand=True, anchor=E)
shuffleFrame.pack(expand=True, anchor=E) shuffleFrame.pack(expand=True, anchor=E)
heartbeepFrame.pack(expand=True, anchor=E) heartbeepFrame.pack(expand=True, anchor=E)
heartcolorFrame.pack(expand=True, anchor=E)
fastMenuFrame.pack(expand=True, anchor=E) fastMenuFrame.pack(expand=True, anchor=E)
bottomFrame = Frame(randomizerWindow) bottomFrame = Frame(randomizerWindow)
@ -243,6 +257,7 @@ def guiMain(args=None):
guiargs.algorithm = algorithmVar.get() guiargs.algorithm = algorithmVar.get()
guiargs.shuffle = shuffleVar.get() guiargs.shuffle = shuffleVar.get()
guiargs.heartbeep = heartbeepVar.get() guiargs.heartbeep = heartbeepVar.get()
guiargs.heartcolor = heartcolorVar.get()
guiargs.fastmenu = fastMenuVar.get() guiargs.fastmenu = fastMenuVar.get()
guiargs.create_spoiler = bool(createSpoilerVar.get()) guiargs.create_spoiler = bool(createSpoilerVar.get())
guiargs.suppress_rom = bool(suppressRomVar.get()) guiargs.suppress_rom = bool(suppressRomVar.get())
@ -252,6 +267,16 @@ def guiMain(args=None):
guiargs.quickswap = bool(quickSwapVar.get()) guiargs.quickswap = bool(quickSwapVar.get())
guiargs.disablemusic = bool(disableMusicVar.get()) guiargs.disablemusic = bool(disableMusicVar.get())
guiargs.shuffleganon = bool(shuffleGanonVar.get()) guiargs.shuffleganon = bool(shuffleGanonVar.get())
guiargs.custom = bool(customVar.get())
guiargs.customitemarray = [int(bowVar.get()), int(silverarrowVar.get()), int(boomerangVar.get()), int(magicboomerangVar.get()), int(hookshotVar.get()), int(mushroomVar.get()), int(magicpowderVar.get()), int(firerodVar.get()),
int(icerodVar.get()), int(bombosVar.get()), int(etherVar.get()), int(quakeVar.get()), int(lampVar.get()), int(hammerVar.get()), int(shovelVar.get()), int(fluteVar.get()), int(bugnetVar.get()),
int(bookVar.get()), int(bottleVar.get()), int(somariaVar.get()), int(byrnaVar.get()), int(capeVar.get()), int(mirrorVar.get()), int(bootsVar.get()), int(powergloveVar.get()), int(titansmittVar.get()),
int(proggloveVar.get()), int(flippersVar.get()), int(pearlVar.get()), int(heartpieceVar.get()), int(fullheartVar.get()), int(sancheartVar.get()), int(sword1Var.get()), int(sword2Var.get()),
int(sword3Var.get()), int(sword4Var.get()), int(progswordVar.get()), int(shield1Var.get()), int(shield2Var.get()), int(shield3Var.get()), int(progshieldVar.get()), int(bluemailVar.get()),
int(redmailVar.get()), int(progmailVar.get()), int(halfmagicVar.get()), int(quartermagicVar.get()), int(bcap5Var.get()), int(bcap10Var.get()), int(acap5Var.get()), int(acap10Var.get()),
int(arrow1Var.get()), int(arrow10Var.get()), int(bomb1Var.get()), int(bomb3Var.get()), int(rupee1Var.get()), int(rupee5Var.get()), int(rupee20Var.get()), int(rupee50Var.get()), int(rupee100Var.get()),
int(rupee300Var.get()), int(rupoorVar.get()), int(blueclockVar.get()), int(greenclockVar.get()), int(redclockVar.get()), int(triforcepieceVar.get()), int(triforcecountVar.get()),
int(triforceVar.get()), int(rupoorcostVar.get())]
guiargs.rom = romVar.get() guiargs.rom = romVar.get()
guiargs.jsonout = None guiargs.jsonout = None
guiargs.sprite = sprite guiargs.sprite = sprite
@ -337,6 +362,12 @@ def guiMain(args=None):
heartbeepLabel2 = Label(heartbeepFrame2, text='Heartbeep sound rate') heartbeepLabel2 = Label(heartbeepFrame2, text='Heartbeep sound rate')
heartbeepLabel2.pack(side=LEFT) heartbeepLabel2.pack(side=LEFT)
heartcolorFrame2 = Frame(drowDownFrame2)
heartcolorOptionMenu2 = OptionMenu(heartcolorFrame2, heartcolorVar, 'red', 'blue', 'green', 'yellow')
heartcolorOptionMenu2.pack(side=RIGHT)
heartcolorLabel2 = Label(heartcolorFrame2, text='Heart color')
heartcolorLabel2.pack(side=LEFT)
fastMenuFrame2 = Frame(drowDownFrame2) fastMenuFrame2 = Frame(drowDownFrame2)
fastMenuOptionMenu2 = OptionMenu(fastMenuFrame2, fastMenuVar, 'normal', 'instant', 'double', 'triple', 'quadruple', 'half') fastMenuOptionMenu2 = OptionMenu(fastMenuFrame2, fastMenuVar, 'normal', 'instant', 'double', 'triple', 'quadruple', 'half')
fastMenuOptionMenu2.pack(side=RIGHT) fastMenuOptionMenu2.pack(side=RIGHT)
@ -344,6 +375,7 @@ def guiMain(args=None):
fastMenuLabel2.pack(side=LEFT) fastMenuLabel2.pack(side=LEFT)
heartbeepFrame2.pack(expand=True, anchor=E) heartbeepFrame2.pack(expand=True, anchor=E)
heartcolorFrame2.pack(expand=True, anchor=E)
fastMenuFrame2.pack(expand=True, anchor=E) fastMenuFrame2.pack(expand=True, anchor=E)
bottomFrame2 = Frame(topFrame2) bottomFrame2 = Frame(topFrame2)
@ -351,6 +383,7 @@ def guiMain(args=None):
def adjustRom(): def adjustRom():
guiargs = Namespace guiargs = Namespace
guiargs.heartbeep = heartbeepVar.get() guiargs.heartbeep = heartbeepVar.get()
guiargs.heartcolor = heartcolorVar.get()
guiargs.fastmenu = fastMenuVar.get() guiargs.fastmenu = fastMenuVar.get()
guiargs.quickswap = bool(quickSwapVar.get()) guiargs.quickswap = bool(quickSwapVar.get())
guiargs.disablemusic = bool(disableMusicVar.get()) guiargs.disablemusic = bool(disableMusicVar.get())
@ -369,9 +402,577 @@ def guiMain(args=None):
drowDownFrame2.pack(side=LEFT, pady=(0, 40)) drowDownFrame2.pack(side=LEFT, pady=(0, 40))
rightHalfFrame2.pack(side=RIGHT) rightHalfFrame2.pack(side=RIGHT)
topFrame2.pack(side=TOP, pady=30) topFrame2.pack(side=TOP, pady=70)
bottomFrame2.pack(side=BOTTOM, pady=(180, 0)) bottomFrame2.pack(side=BOTTOM, pady=(180, 0))
# Custom Controls
topFrame3 = Frame(customWindow)
def validation(P):
if str.isdigit(P) or P == "":
return True
else:
return False
vcmd=(topFrame3.register(validation), '%P')
itemList1 = Frame(topFrame3)
itemList2 = Frame(topFrame3)
itemList3 = Frame(topFrame3)
itemList4 = Frame(topFrame3)
itemList5 = Frame(topFrame3)
bowFrame = Frame(itemList1)
bowLabel = Label(bowFrame, text='Bow')
bowVar = StringVar(value='1')
bowEntry = Entry(bowFrame, textvariable=bowVar, width=3, validate='all', vcmd=vcmd)
bowFrame.pack()
bowLabel.pack(anchor=W, side=LEFT, padx=(0,53))
bowEntry.pack(anchor=E)
silverarrowFrame = Frame(itemList1)
silverarrowLabel = Label(silverarrowFrame, text='Silver Arrow')
silverarrowVar = StringVar(value='1')
silverarrowEntry = Entry(silverarrowFrame, textvariable=silverarrowVar, width=3, validate='all', vcmd=vcmd)
silverarrowFrame.pack()
silverarrowLabel.pack(anchor=W, side=LEFT, padx=(0,13))
silverarrowEntry.pack(anchor=E)
boomerangFrame = Frame(itemList1)
boomerangLabel = Label(boomerangFrame, text='Boomerang')
boomerangVar = StringVar(value='1')
boomerangEntry = Entry(boomerangFrame, textvariable=boomerangVar, width=3, validate='all', vcmd=vcmd)
boomerangFrame.pack()
boomerangLabel.pack(anchor=W, side=LEFT, padx=(0,14))
boomerangEntry.pack(anchor=E)
magicboomerangFrame = Frame(itemList1)
magicboomerangLabel = Label(magicboomerangFrame, text='M.Boomerang')
magicboomerangVar = StringVar(value='1')
magicboomerangEntry = Entry(magicboomerangFrame, textvariable=magicboomerangVar, width=3, validate='all', vcmd=vcmd)
magicboomerangFrame.pack()
magicboomerangLabel.pack(anchor=W, side=LEFT)
magicboomerangEntry.pack(anchor=E)
hookshotFrame = Frame(itemList1)
hookshotLabel = Label(hookshotFrame, text='Hookshot')
hookshotVar = StringVar(value='1')
hookshotEntry = Entry(hookshotFrame, textvariable=hookshotVar, width=3, validate='all', vcmd=vcmd)
hookshotFrame.pack()
hookshotLabel.pack(anchor=W, side=LEFT, padx=(0,24))
hookshotEntry.pack(anchor=E)
mushroomFrame = Frame(itemList1)
mushroomLabel = Label(mushroomFrame, text='Mushroom')
mushroomVar = StringVar(value='1')
mushroomEntry = Entry(mushroomFrame, textvariable=mushroomVar, width=3, validate='all', vcmd=vcmd)
mushroomFrame.pack()
mushroomLabel.pack(anchor=W, side=LEFT, padx=(0,17))
mushroomEntry.pack(anchor=E)
magicpowderFrame = Frame(itemList1)
magicpowderLabel = Label(magicpowderFrame, text='Magic Powder')
magicpowderVar = StringVar(value='1')
magicpowderEntry = Entry(magicpowderFrame, textvariable=magicpowderVar, width=3, validate='all', vcmd=vcmd)
magicpowderFrame.pack()
magicpowderLabel.pack(anchor=W, side=LEFT)
magicpowderEntry.pack(anchor=E)
firerodFrame = Frame(itemList1)
firerodLabel = Label(firerodFrame, text='Fire Rod')
firerodVar = StringVar(value='1')
firerodEntry = Entry(firerodFrame, textvariable=firerodVar, width=3, validate='all', vcmd=vcmd)
firerodFrame.pack()
firerodLabel.pack(anchor=W, side=LEFT, padx=(0,33))
firerodEntry.pack(anchor=E)
icerodFrame = Frame(itemList1)
icerodLabel = Label(icerodFrame, text='Ice Rod')
icerodVar = StringVar(value='1')
icerodEntry = Entry(icerodFrame, textvariable=icerodVar, width=3, validate='all', vcmd=vcmd)
icerodFrame.pack()
icerodLabel.pack(anchor=W, side=LEFT, padx=(0,37))
icerodEntry.pack(anchor=E)
bombosFrame = Frame(itemList1)
bombosLabel = Label(bombosFrame, text='Bombos')
bombosVar = StringVar(value='1')
bombosEntry = Entry(bombosFrame, textvariable=bombosVar, width=3, validate='all', vcmd=vcmd)
bombosFrame.pack()
bombosLabel.pack(anchor=W, side=LEFT, padx=(0,32))
bombosEntry.pack(anchor=E)
etherFrame = Frame(itemList1)
etherLabel = Label(etherFrame, text='Ether')
etherVar = StringVar(value='1')
etherEntry = Entry(etherFrame, textvariable=etherVar, width=3, validate='all', vcmd=vcmd)
etherFrame.pack()
etherLabel.pack(anchor=W, side=LEFT, padx=(0,49))
etherEntry.pack(anchor=E)
quakeFrame = Frame(itemList1)
quakeLabel = Label(quakeFrame, text='Quake')
quakeVar = StringVar(value='1')
quakeEntry = Entry(quakeFrame, textvariable=quakeVar, width=3, validate='all', vcmd=vcmd)
quakeFrame.pack()
quakeLabel.pack(anchor=W, side=LEFT, padx=(0,42))
quakeEntry.pack(anchor=E)
lampFrame = Frame(itemList1)
lampLabel = Label(lampFrame, text='Lamp')
lampVar = StringVar(value='1')
lampEntry = Entry(lampFrame, textvariable=lampVar, width=3, validate='all', vcmd=vcmd)
lampFrame.pack()
lampLabel.pack(anchor=W, side=LEFT, padx=(0,46))
lampEntry.pack(anchor=E)
hammerFrame = Frame(itemList1)
hammerLabel = Label(hammerFrame, text='Hammer')
hammerVar = StringVar(value='1')
hammerEntry = Entry(hammerFrame, textvariable=hammerVar, width=3, validate='all', vcmd=vcmd)
hammerFrame.pack()
hammerLabel.pack(anchor=W, side=LEFT, padx=(0,29))
hammerEntry.pack(anchor=E)
shovelFrame = Frame(itemList1)
shovelLabel = Label(shovelFrame, text='Shovel')
shovelVar = StringVar(value='1')
shovelEntry = Entry(shovelFrame, textvariable=shovelVar, width=3, validate='all', vcmd=vcmd)
shovelFrame.pack()
shovelLabel.pack(anchor=W, side=LEFT, padx=(0,41))
shovelEntry.pack(anchor=E)
fluteFrame = Frame(itemList1)
fluteLabel = Label(fluteFrame, text='Flute')
fluteVar = StringVar(value='1')
fluteEntry = Entry(fluteFrame, textvariable=fluteVar, width=3, validate='all', vcmd=vcmd)
fluteFrame.pack()
fluteLabel.pack(anchor=W, side=LEFT, padx=(0,50))
fluteEntry.pack(anchor=E)
bugnetFrame = Frame(itemList2)
bugnetLabel = Label(bugnetFrame, text='Bug Net')
bugnetVar = StringVar(value='1')
bugnetEntry = Entry(bugnetFrame, textvariable=bugnetVar, width=3, validate='all', vcmd=vcmd)
bugnetFrame.pack()
bugnetLabel.pack(anchor=W, side=LEFT, padx=(0,41))
bugnetEntry.pack(anchor=E)
bookFrame = Frame(itemList2)
bookLabel = Label(bookFrame, text='Book')
bookVar = StringVar(value='1')
bookEntry = Entry(bookFrame, textvariable=bookVar, width=3, validate='all', vcmd=vcmd)
bookFrame.pack()
bookLabel.pack(anchor=W, side=LEFT, padx=(0,57))
bookEntry.pack(anchor=E)
bottleFrame = Frame(itemList2)
bottleLabel = Label(bottleFrame, text='Bottle')
bottleVar = StringVar(value='4')
bottleEntry = Entry(bottleFrame, textvariable=bottleVar, width=3, validate='all', vcmd=vcmd)
bottleFrame.pack()
bottleLabel.pack(anchor=W, side=LEFT, padx=(0,53))
bottleEntry.pack(anchor=E)
somariaFrame = Frame(itemList2)
somariaLabel = Label(somariaFrame, text='C.Somaria')
somariaVar = StringVar(value='1')
somariaEntry = Entry(somariaFrame, textvariable=somariaVar, width=3, validate='all', vcmd=vcmd)
somariaFrame.pack()
somariaLabel.pack(anchor=W, side=LEFT, padx=(0,30))
somariaEntry.pack(anchor=E)
byrnaFrame = Frame(itemList2)
byrnaLabel = Label(byrnaFrame, text='C.Byrna')
byrnaVar = StringVar(value='1')
byrnaEntry = Entry(byrnaFrame, textvariable=byrnaVar, width=3, validate='all', vcmd=vcmd)
byrnaFrame.pack()
byrnaLabel.pack(anchor=W, side=LEFT, padx=(0,43))
byrnaEntry.pack(anchor=E)
capeFrame = Frame(itemList2)
capeLabel = Label(capeFrame, text='Magic Cape')
capeVar = StringVar(value='1')
capeEntry = Entry(capeFrame, textvariable=capeVar, width=3, validate='all', vcmd=vcmd)
capeFrame.pack()
capeLabel.pack(anchor=W, side=LEFT, padx=(0,21))
capeEntry.pack(anchor=E)
mirrorFrame = Frame(itemList2)
mirrorLabel = Label(mirrorFrame, text='Magic Mirror')
mirrorVar = StringVar(value='1')
mirrorEntry = Entry(mirrorFrame, textvariable=mirrorVar, width=3, validate='all', vcmd=vcmd)
mirrorFrame.pack()
mirrorLabel.pack(anchor=W, side=LEFT, padx=(0,15))
mirrorEntry.pack(anchor=E)
bootsFrame = Frame(itemList2)
bootsLabel = Label(bootsFrame, text='Pegasus Boots')
bootsVar = StringVar(value='1')
bootsEntry = Entry(bootsFrame, textvariable=bootsVar, width=3, validate='all', vcmd=vcmd)
bootsFrame.pack()
bootsLabel.pack(anchor=W, side=LEFT, padx=(0,8))
bootsEntry.pack(anchor=E)
powergloveFrame = Frame(itemList2)
powergloveLabel = Label(powergloveFrame, text='Power Glove')
powergloveVar = StringVar(value='0')
powergloveEntry = Entry(powergloveFrame, textvariable=powergloveVar, width=3, validate='all', vcmd=vcmd)
powergloveFrame.pack()
powergloveLabel.pack(anchor=W, side=LEFT, padx=(0,18))
powergloveEntry.pack(anchor=E)
titansmittFrame = Frame(itemList2)
titansmittLabel = Label(titansmittFrame, text='Titan\'s Mitt')
titansmittVar = StringVar(value='0')
titansmittEntry = Entry(titansmittFrame, textvariable=titansmittVar, width=3, validate='all', vcmd=vcmd)
titansmittFrame.pack()
titansmittLabel.pack(anchor=W, side=LEFT, padx=(0,24))
titansmittEntry.pack(anchor=E)
proggloveFrame = Frame(itemList2)
proggloveLabel = Label(proggloveFrame, text='Prog.Glove')
proggloveVar = StringVar(value='2')
proggloveEntry = Entry(proggloveFrame, textvariable=proggloveVar, width=3, validate='all', vcmd=vcmd)
proggloveFrame.pack()
proggloveLabel.pack(anchor=W, side=LEFT, padx=(0,26))
proggloveEntry.pack(anchor=E)
flippersFrame = Frame(itemList2)
flippersLabel = Label(flippersFrame, text='Flippers')
flippersVar = StringVar(value='1')
flippersEntry = Entry(flippersFrame, textvariable=flippersVar, width=3, validate='all', vcmd=vcmd)
flippersFrame.pack()
flippersLabel.pack(anchor=W, side=LEFT, padx=(0,43))
flippersEntry.pack(anchor=E)
pearlFrame = Frame(itemList2)
pearlLabel = Label(pearlFrame, text='Moon Pearl')
pearlVar = StringVar(value='1')
pearlEntry = Entry(pearlFrame, textvariable=pearlVar, width=3, validate='all', vcmd=vcmd)
pearlFrame.pack()
pearlLabel.pack(anchor=W, side=LEFT, padx=(0,23))
pearlEntry.pack(anchor=E)
heartpieceFrame = Frame(itemList2)
heartpieceLabel = Label(heartpieceFrame, text='Piece of Heart')
heartpieceVar = StringVar(value='24')
heartpieceEntry = Entry(heartpieceFrame, textvariable=heartpieceVar, width=3, validate='all', vcmd=vcmd)
heartpieceFrame.pack()
heartpieceLabel.pack(anchor=W, side=LEFT, padx=(0,10))
heartpieceEntry.pack(anchor=E)
fullheartFrame = Frame(itemList2)
fullheartLabel = Label(fullheartFrame, text='Heart Container')
fullheartVar = StringVar(value='10')
fullheartEntry = Entry(fullheartFrame, textvariable=fullheartVar, width=3, validate='all', vcmd=vcmd)
fullheartFrame.pack()
fullheartLabel.pack(anchor=W, side=LEFT)
fullheartEntry.pack(anchor=E)
sancheartFrame = Frame(itemList2)
sancheartLabel = Label(sancheartFrame, text='Sanctuary Heart')
sancheartVar = StringVar(value='1')
sancheartEntry = Entry(sancheartFrame, textvariable=sancheartVar, width=3, validate='all', vcmd=vcmd)
sancheartFrame.pack()
sancheartLabel.pack(anchor=W, side=LEFT)
sancheartEntry.pack(anchor=E)
sword1Frame = Frame(itemList3)
sword1Label = Label(sword1Frame, text='Sword 1')
sword1Var = StringVar(value='0')
sword1Entry = Entry(sword1Frame, textvariable=sword1Var, width=3, validate='all', vcmd=vcmd)
sword1Frame.pack()
sword1Label.pack(anchor=W, side=LEFT, padx=(0,34))
sword1Entry.pack(anchor=E)
sword2Frame = Frame(itemList3)
sword2Label = Label(sword2Frame, text='Sword 2')
sword2Var = StringVar(value='0')
sword2Entry = Entry(sword2Frame, textvariable=sword2Var, width=3, validate='all', vcmd=vcmd)
sword2Frame.pack()
sword2Label.pack(anchor=W, side=LEFT, padx=(0,34))
sword2Entry.pack(anchor=E)
sword3Frame = Frame(itemList3)
sword3Label = Label(sword3Frame, text='Sword 3')
sword3Var = StringVar(value='0')
sword3Entry = Entry(sword3Frame, textvariable=sword3Var, width=3, validate='all', vcmd=vcmd)
sword3Frame.pack()
sword3Label.pack(anchor=W, side=LEFT, padx=(0,34))
sword3Entry.pack(anchor=E)
sword4Frame = Frame(itemList3)
sword4Label = Label(sword4Frame, text='Sword 4')
sword4Var = StringVar(value='0')
sword4Entry = Entry(sword4Frame, textvariable=sword4Var, width=3, validate='all', vcmd=vcmd)
sword4Frame.pack()
sword4Label.pack(anchor=W, side=LEFT, padx=(0,34))
sword4Entry.pack(anchor=E)
progswordFrame = Frame(itemList3)
progswordLabel = Label(progswordFrame, text='Prog.Sword')
progswordVar = StringVar(value='4')
progswordEntry = Entry(progswordFrame, textvariable=progswordVar, width=3, validate='all', vcmd=vcmd)
progswordFrame.pack()
progswordLabel.pack(anchor=W, side=LEFT, padx=(0,15))
progswordEntry.pack(anchor=E)
shield1Frame = Frame(itemList3)
shield1Label = Label(shield1Frame, text='Shield 1')
shield1Var = StringVar(value='0')
shield1Entry = Entry(shield1Frame, textvariable=shield1Var, width=3, validate='all', vcmd=vcmd)
shield1Frame.pack()
shield1Label.pack(anchor=W, side=LEFT, padx=(0,35))
shield1Entry.pack(anchor=E)
shield2Frame = Frame(itemList3)
shield2Label = Label(shield2Frame, text='Shield 2')
shield2Var = StringVar(value='0')
shield2Entry = Entry(shield2Frame, textvariable=shield2Var, width=3, validate='all', vcmd=vcmd)
shield2Frame.pack()
shield2Label.pack(anchor=W, side=LEFT, padx=(0,35))
shield2Entry.pack(anchor=E)
shield3Frame = Frame(itemList3)
shield3Label = Label(shield3Frame, text='Shield 3')
shield3Var = StringVar(value='0')
shield3Entry = Entry(shield3Frame, textvariable=shield3Var, width=3, validate='all', vcmd=vcmd)
shield3Frame.pack()
shield3Label.pack(anchor=W, side=LEFT, padx=(0,35))
shield3Entry.pack(anchor=E)
progshieldFrame = Frame(itemList3)
progshieldLabel = Label(progshieldFrame, text='Prog.Shield')
progshieldVar = StringVar(value='3')
progshieldEntry = Entry(progshieldFrame, textvariable=progshieldVar, width=3, validate='all', vcmd=vcmd)
progshieldFrame.pack()
progshieldLabel.pack(anchor=W, side=LEFT, padx=(0,16))
progshieldEntry.pack(anchor=E)
bluemailFrame = Frame(itemList3)
bluemailLabel = Label(bluemailFrame, text='Blue Mail')
bluemailVar = StringVar(value='0')
bluemailEntry = Entry(bluemailFrame, textvariable=bluemailVar, width=3, validate='all', vcmd=vcmd)
bluemailFrame.pack()
bluemailLabel.pack(anchor=W, side=LEFT, padx=(0,27))
bluemailEntry.pack(anchor=E)
redmailFrame = Frame(itemList3)
redmailLabel = Label(redmailFrame, text='Red Mail')
redmailVar = StringVar(value='0')
redmailEntry = Entry(redmailFrame, textvariable=redmailVar, width=3, validate='all', vcmd=vcmd)
redmailFrame.pack()
redmailLabel.pack(anchor=W, side=LEFT, padx=(0,30))
redmailEntry.pack(anchor=E)
progmailFrame = Frame(itemList3)
progmailLabel = Label(progmailFrame, text='Prog.Mail')
progmailVar = StringVar(value='2')
progmailEntry = Entry(progmailFrame, textvariable=progmailVar, width=3, validate='all', vcmd=vcmd)
progmailFrame.pack()
progmailLabel.pack(anchor=W, side=LEFT, padx=(0,25))
progmailEntry.pack(anchor=E)
halfmagicFrame = Frame(itemList3)
halfmagicLabel = Label(halfmagicFrame, text='Half Magic')
halfmagicVar = StringVar(value='1')
halfmagicEntry = Entry(halfmagicFrame, textvariable=halfmagicVar, width=3, validate='all', vcmd=vcmd)
halfmagicFrame.pack()
halfmagicLabel.pack(anchor=W, side=LEFT, padx=(0,18))
halfmagicEntry.pack(anchor=E)
quartermagicFrame = Frame(itemList3)
quartermagicLabel = Label(quartermagicFrame, text='Quarter Magic')
quartermagicVar = StringVar(value='0')
quartermagicEntry = Entry(quartermagicFrame, textvariable=quartermagicVar, width=3, validate='all', vcmd=vcmd)
quartermagicFrame.pack()
quartermagicLabel.pack(anchor=W, side=LEFT)
quartermagicEntry.pack(anchor=E)
bcap5Frame = Frame(itemList3)
bcap5Label = Label(bcap5Frame, text='Bomb C.+5')
bcap5Var = StringVar(value='6')
bcap5Entry = Entry(bcap5Frame, textvariable=bcap5Var, width=3, validate='all', vcmd=vcmd)
bcap5Frame.pack()
bcap5Label.pack(anchor=W, side=LEFT, padx=(0,16))
bcap5Entry.pack(anchor=E)
bcap10Frame = Frame(itemList3)
bcap10Label = Label(bcap10Frame, text='Bomb C.+10')
bcap10Var = StringVar(value='1')
bcap10Entry = Entry(bcap10Frame, textvariable=bcap10Var, width=3, validate='all', vcmd=vcmd)
bcap10Frame.pack()
bcap10Label.pack(anchor=W, side=LEFT, padx=(0,10))
bcap10Entry.pack(anchor=E)
acap5Frame = Frame(itemList4)
acap5Label = Label(acap5Frame, text='Arrow C.+5')
acap5Var = StringVar(value='6')
acap5Entry = Entry(acap5Frame, textvariable=acap5Var, width=3, validate='all', vcmd=vcmd)
acap5Frame.pack()
acap5Label.pack(anchor=W, side=LEFT, padx=(0,7))
acap5Entry.pack(anchor=E)
acap10Frame = Frame(itemList4)
acap10Label = Label(acap10Frame, text='Arrow C.+10')
acap10Var = StringVar(value='1')
acap10Entry = Entry(acap10Frame, textvariable=acap10Var, width=3, validate='all', vcmd=vcmd)
acap10Frame.pack()
acap10Label.pack(anchor=W, side=LEFT, padx=(0,1))
acap10Entry.pack(anchor=E)
arrow1Frame = Frame(itemList4)
arrow1Label = Label(arrow1Frame, text='Arrow (1)')
arrow1Var = StringVar(value='1')
arrow1Entry = Entry(arrow1Frame, textvariable=arrow1Var, width=3, validate='all', vcmd=vcmd)
arrow1Frame.pack()
arrow1Label.pack(anchor=W, side=LEFT, padx=(0,18))
arrow1Entry.pack(anchor=E)
arrow10Frame = Frame(itemList4)
arrow10Label = Label(arrow10Frame, text='Arrows (10)')
arrow10Var = StringVar(value='5')
arrow10Entry = Entry(arrow10Frame, textvariable=arrow10Var, width=3, validate='all', vcmd=vcmd)
arrow10Frame.pack()
arrow10Label.pack(anchor=W, side=LEFT, padx=(0,7))
arrow10Entry.pack(anchor=E)
bomb1Frame = Frame(itemList4)
bomb1Label = Label(bomb1Frame, text='Bomb (1)')
bomb1Var = StringVar(value='0')
bomb1Entry = Entry(bomb1Frame, textvariable=bomb1Var, width=3, validate='all', vcmd=vcmd)
bomb1Frame.pack()
bomb1Label.pack(anchor=W, side=LEFT, padx=(0,18))
bomb1Entry.pack(anchor=E)
bomb3Frame = Frame(itemList4)
bomb3Label = Label(bomb3Frame, text='Bombs (3)')
bomb3Var = StringVar(value='10')
bomb3Entry = Entry(bomb3Frame, textvariable=bomb3Var, width=3, validate='all', vcmd=vcmd)
bomb3Frame.pack()
bomb3Label.pack(anchor=W, side=LEFT, padx=(0,13))
bomb3Entry.pack(anchor=E)
rupee1Frame = Frame(itemList4)
rupee1Label = Label(rupee1Frame, text='Rupee (1)')
rupee1Var = StringVar(value='2')
rupee1Entry = Entry(rupee1Frame, textvariable=rupee1Var, width=3, validate='all', vcmd=vcmd)
rupee1Frame.pack()
rupee1Label.pack(anchor=W, side=LEFT, padx=(0,17))
rupee1Entry.pack(anchor=E)
rupee5Frame = Frame(itemList4)
rupee5Label = Label(rupee5Frame, text='Rupees (5)')
rupee5Var = StringVar(value='4')
rupee5Entry = Entry(rupee5Frame, textvariable=rupee5Var, width=3, validate='all', vcmd=vcmd)
rupee5Frame.pack()
rupee5Label.pack(anchor=W, side=LEFT, padx=(0,12))
rupee5Entry.pack(anchor=E)
rupee20Frame = Frame(itemList4)
rupee20Label = Label(rupee20Frame, text='Rupees (20)')
rupee20Var = StringVar(value='28')
rupee20Entry = Entry(rupee20Frame, textvariable=rupee20Var, width=3, validate='all', vcmd=vcmd)
rupee20Frame.pack()
rupee20Label.pack(anchor=W, side=LEFT, padx=(0,6))
rupee20Entry.pack(anchor=E)
rupee50Frame = Frame(itemList4)
rupee50Label = Label(rupee50Frame, text='Rupees (50)')
rupee50Var = StringVar(value='7')
rupee50Entry = Entry(rupee50Frame, textvariable=rupee50Var, width=3, validate='all', vcmd=vcmd)
rupee50Frame.pack()
rupee50Label.pack(anchor=W, side=LEFT, padx=(0,6))
rupee50Entry.pack(anchor=E)
rupee100Frame = Frame(itemList4)
rupee100Label = Label(rupee100Frame, text='Rupees (100)')
rupee100Var = StringVar(value='1')
rupee100Entry = Entry(rupee100Frame, textvariable=rupee100Var, width=3, validate='all', vcmd=vcmd)
rupee100Frame.pack()
rupee100Label.pack(anchor=W, side=LEFT, padx=(0,0))
rupee100Entry.pack(anchor=E)
rupee300Frame = Frame(itemList4)
rupee300Label = Label(rupee300Frame, text='Rupees (300)')
rupee300Var = StringVar(value='5')
rupee300Entry = Entry(rupee300Frame, textvariable=rupee300Var, width=3, validate='all', vcmd=vcmd)
rupee300Frame.pack()
rupee300Label.pack(anchor=W, side=LEFT, padx=(0,0))
rupee300Entry.pack(anchor=E)
rupoorFrame = Frame(itemList4)
rupoorLabel = Label(rupoorFrame, text='Rupoor')
rupoorVar = StringVar(value='0')
rupoorEntry = Entry(rupoorFrame, textvariable=rupoorVar, width=3, validate='all', vcmd=vcmd)
rupoorFrame.pack()
rupoorLabel.pack(anchor=W, side=LEFT, padx=(0,28))
rupoorEntry.pack(anchor=E)
blueclockFrame = Frame(itemList4)
blueclockLabel = Label(blueclockFrame, text='Blue Clock')
blueclockVar = StringVar(value='0')
blueclockEntry = Entry(blueclockFrame, textvariable=blueclockVar, width=3, validate='all', vcmd=vcmd)
blueclockFrame.pack()
blueclockLabel.pack(anchor=W, side=LEFT, padx=(0,11))
blueclockEntry.pack(anchor=E)
greenclockFrame = Frame(itemList4)
greenclockLabel = Label(greenclockFrame, text='Green Clock')
greenclockVar = StringVar(value='0')
greenclockEntry = Entry(greenclockFrame, textvariable=greenclockVar, width=3, validate='all', vcmd=vcmd)
greenclockFrame.pack()
greenclockLabel.pack(anchor=W, side=LEFT, padx=(0,3))
greenclockEntry.pack(anchor=E)
redclockFrame = Frame(itemList4)
redclockLabel = Label(redclockFrame, text='Red Clock')
redclockVar = StringVar(value='0')
redclockEntry = Entry(redclockFrame, textvariable=redclockVar, width=3, validate='all', vcmd=vcmd)
redclockFrame.pack()
redclockLabel.pack(anchor=W, side=LEFT, padx=(0,14))
redclockEntry.pack(anchor=E)
triforcepieceFrame = Frame(itemList5)
triforcepieceLabel = Label(triforcepieceFrame, text='Triforce Piece')
triforcepieceVar = StringVar(value='0')
triforcepieceEntry = Entry(triforcepieceFrame, textvariable=triforcepieceVar, width=3, validate='all', vcmd=vcmd)
triforcepieceFrame.pack()
triforcepieceLabel.pack(anchor=W, side=LEFT, padx=(0,55))
triforcepieceEntry.pack(anchor=E)
triforcecountFrame = Frame(itemList5)
triforcecountLabel = Label(triforcecountFrame, text='Triforce Pieces Required')
triforcecountVar = StringVar(value='0')
triforcecountEntry = Entry(triforcecountFrame, textvariable=triforcecountVar, width=3, validate='all', vcmd=vcmd)
triforcecountFrame.pack()
triforcecountLabel.pack(anchor=W, side=LEFT, padx=(0,0))
triforcecountEntry.pack(anchor=E)
triforceFrame = Frame(itemList5)
triforceLabel = Label(triforceFrame, text='Triforce (win game)')
triforceVar = StringVar(value='0')
triforceEntry = Entry(triforceFrame, textvariable=triforceVar, width=3, validate='all', vcmd=vcmd)
triforceFrame.pack()
triforceLabel.pack(anchor=W, side=LEFT, padx=(0,23))
triforceEntry.pack(anchor=E)
rupoorcostFrame = Frame(itemList5)
rupoorcostLabel = Label(rupoorcostFrame, text='Rupoor Cost')
rupoorcostVar = StringVar(value='10')
rupoorcostEntry = Entry(rupoorcostFrame, textvariable=rupoorcostVar, width=6, validate='all', vcmd=vcmd)
rupoorcostFrame.pack()
rupoorcostLabel.pack(anchor=W, side=LEFT, padx=(0,43))
rupoorcostEntry.pack(anchor=E)
itemList1.pack(side=LEFT, padx=(0,0))
itemList2.pack(side=LEFT, padx=(0,0))
itemList3.pack(side=LEFT, padx=(0,0))
itemList4.pack(side=LEFT, padx=(0,0))
itemList5.pack(side=LEFT, padx=(0,0))
topFrame3.pack(side=TOP, pady=(17,0))
if args is not None: if args is not None:
# load values from commandline args # load values from commandline args
createSpoilerVar.set(int(args.create_spoiler)) createSpoilerVar.set(int(args.create_spoiler))

View File

@ -1,8 +1,9 @@
from collections import namedtuple from collections import namedtuple
import logging
import random import random
from Items import ItemFactory from Items import ItemFactory
from Fill import fill_restrictive from Fill import FillError, fill_restrictive
from Dungeons import get_dungeon_item_pool from Dungeons import get_dungeon_item_pool
#This file sets the item pools for various modes. Timed modes and triforce hunt are enforced first, and then extra items are specified per mode to fill in the remaining space. #This file sets the item pools for various modes. Timed modes and triforce hunt are enforced first, and then extra items are specified per mode to fill in the remaining space.
@ -25,8 +26,8 @@ normalfourth5extra = ['Arrows (10)'] * 2 + ['Rupees (20)'] * 2 + ['Rupees (5)']
normalfinal25extra = ['Rupees (20)'] * 23 + ['Rupees (5)'] * 2 normalfinal25extra = ['Rupees (20)'] * 23 + ['Rupees (5)'] * 2
easybaseitems = (['Blue Boomerang', 'Red Boomerang', 'Silver Arrows'] + ['Rupees (300)'] * 4 + ['Magic Upgrade (1/2)'] * 2 + ['Lamp'] * 2 + easybaseitems = (['Blue Boomerang', 'Red Boomerang', 'Sanctuary Heart Container'] + ['Rupees (300)'] * 4 + ['Magic Upgrade (1/2)'] * 2 + ['Lamp'] * 2 +
['Single Arrow', 'Sanctuary Heart Container'] + ['Boss Heart Container'] * 10 + ['Piece of Heart'] * 12) ['Silver Arrows'] * 2 + ['Boss Heart Container'] * 10 + ['Piece of Heart'] * 12)
easyextra = ['Piece of Heart'] * 12 + ['Rupees (300)'] easyextra = ['Piece of Heart'] * 12 + ['Rupees (300)']
easylimitedextra = ['Boss Heart Container'] * 3 # collapsing down the 12 pieces of heart easylimitedextra = ['Boss Heart Container'] * 3 # collapsing down the 12 pieces of heart
easyfirst15extra = ['Rupees (100)', 'Arrow Upgrade (+10)', 'Bomb Upgrade (+10)'] + ['Arrow Upgrade (+5)'] * 6 + ['Bomb Upgrade (+5)'] * 6 easyfirst15extra = ['Rupees (100)', 'Arrow Upgrade (+10)', 'Bomb Upgrade (+10)'] + ['Arrow Upgrade (+5)'] * 6 + ['Bomb Upgrade (+5)'] * 6
@ -35,26 +36,28 @@ easythird5extra = ['Rupees (50)'] * 2 + ['Bombs (3)'] * 2 + ['Arrows (10)']
easyfinal25extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 14 + ['Rupee (1)'] + ['Arrows (10)'] * 4 + ['Rupees (5)'] * 2 easyfinal25extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 14 + ['Rupee (1)'] + ['Arrows (10)'] * 4 + ['Rupees (5)'] * 2
easytimedotherextra = ['Red Clock'] * 5 easytimedotherextra = ['Red Clock'] * 5
hardbaseitems = (['Silver Arrows', 'Single Arrow'] + ['Rupees (300)'] + ['Rupees (100)'] * 2 + ['Rupees (50)'] + ['Bombs (3)'] + hardbaseitems = (['Silver Arrows', 'Single Arrow', 'Single Bomb'] + ['Rupees (300)'] + ['Rupees (100)'] * 3 + ['Rupees (50)'] * 5 + ['Bombs (3)'] * 5 +
['Boss Heart Container'] * 5 + ['Piece of Heart'] * 24) ['Boss Heart Container'] * 5 + ['Piece of Heart'] * 24)
hardfirst20extra = ['Bombs (3)'] * 4 + ['Single Bomb'] * 4 + ['Rupees (5)'] * 5 + ['Rupee (1)'] * 2 + ['Rupees (100)'] + ['Rupees (50)'] * 4 hardfirst20extra = ['Single Bomb'] * 7 + ['Rupees (5)'] * 8 + ['Rupee (1)'] * 2 + ['Rupees (20)'] * 2 + ['Arrows (10)']
hardsecond20extra = ['Single Bomb'] * 4 + ['Rupees (5)'] * 10 + ['Rupees (20)'] * 2 + ['Rupee (1)'] * 3 + ['Arrows (10)'] hardsecond10extra = ['Rupees (5)'] * 7 + ['Rupee (1)'] * 3
hardthird20extra = ['Arrows (10)'] * 4 + ['Rupees (20)'] * 3 + ['Rupees (5)'] * 3 + ['Single Bomb'] * 5 + ['Single Arrow'] * 5 hardthird10extra = ['Arrows (10)'] * 4 + ['Rupees (20)'] * 3 + ['Single Bomb'] * 3
hardfourth10extra = ['Rupees (5)'] * 3 + ['Single Arrow'] * 5 + ['Single Bomb'] * 2
hardfinal20extra = ['Single Bomb'] * 4 + ['Rupees (5)'] * 2 + ['Single Arrow'] * 14 hardfinal20extra = ['Single Bomb'] * 4 + ['Rupees (5)'] * 2 + ['Single Arrow'] * 14
expertbaseitems = (['Single Arrow', 'Rupees (300)', 'Rupees (100)', 'Bombs (3)', 'Arrows (10)'] + ['Rupees (50)'] * 4 + ['Rupees (5)'] * 5 + expertbaseitems = (['Single Arrow', 'Rupees (300)', 'Rupees (100)', 'Bombs (3)', 'Arrows (10)'] + ['Rupees (50)'] * 4 + ['Rupees (5)'] * 5 +
['Rupees (20)'] + ['Single Bomb'] * 2 + ['Piece of Heart'] * 24) ['Rupees (20)'] * 3 + ['Single Bomb'] * 10 + ['Piece of Heart'] * 24)
expertfirst15extra = ['Single Bomb'] * 13 + ['Rupees (20)'] * 2 expertfirst15extra = ['Single Bomb'] * 7 + ['Rupees (20)'] * 3 + ['Single Arrow'] * 5
expertsecond25extra = ['Single Bomb'] * 8 + ['Single Arrow'] * 9 + ['Rupees (20)'] * 3 + ['Rupee (1)'] * 5 expertsecond15extra = ['Single Bomb'] * 6 + ['Single Arrow'] * 4 + ['Rupee (1)'] * 5
expertthird15extra = ['Rupees (5)'] * 5 + ['Single Bomb'] * 3 + ['Rupees (20)'] * 2 + ['Single Arrow'] * 5 expertthird10extra = ['Rupees (5)'] * 3 + ['Single Bomb'] * 3 + ['Rupees (20)'] * 2 + ['Single Arrow'] * 2
expertfourth5extra = ['Rupees (5)'] * 2 + ['Single Arrow'] * 3
expertfinal25extra = ['Single Bomb'] * 4 + ['Rupees (20)'] * 3 + ['Single Arrow'] * 18 expertfinal25extra = ['Single Bomb'] * 4 + ['Rupees (20)'] * 3 + ['Single Arrow'] * 18
insanebaseitems = (['Single Arrow', 'Bombs (3)', 'Arrows (10)'] + ['Rupees (50)'] * 3 + ['Rupees (5)'] * 10 + ['Rupees (300)'] * 4 + ['Rupees (100)'] * 3 + insanebaseitems = (['Bombs (3)', 'Arrows (10)'] + ['Rupees (50)'] * 4 + ['Rupees (5)'] * 10 + ['Rupees (300)'] * 5 + ['Rupees (100)'] * 4 +
['Rupee (1)'] * 4 + ['Single Bomb'] * 4) ['Rupee (1)'] * 8 + ['Rupees (20)'] * 4 + ['Single Bomb'] * 8 + ['Single Arrow'] * 6)
insanefirst15extra = ['Single Bomb'] * 4 + ['Single Arrow'] * 4 + ['Rupee (1)'] * 4 + ['Rupees (300)'] + ['Rupees (100)'] + ['Rupees (50)'] insanefirst15extra = ['Single Bomb'] * 5 + ['Single Arrow'] * 4 + ['Rupee (1)'] * 5 + ['Rupees (20)']
insanesecond25extra = ['Single Bomb'] * 7 + ['Single Arrow'] * 7 + ['Rupee (1)'] * 7 + ['Rupees (20)'] * 4 insanesecond15extra = ['Single Bomb'] * 5 + ['Single Arrow'] * 5 + ['Rupee (1)'] * 5
insanethird10extra = ['Single Bomb'] * 3 + ['Single Arrow'] * 3 + ['Rupee (1)'] * 3 + ['Rupees (20)'] insanethird10extra = ['Single Bomb'] * 4 + ['Single Arrow'] * 3 + ['Rupee (1)'] * 3
insanefourth15extra = ['Single Bomb'] * 5 + ['Single Arrow'] * 5 + ['Rupee (1)'] * 5 insanefourth5extra = ['Single Bomb'] + ['Single Arrow'] * 2 + ['Rupee (1)'] * 2
insanefinal25extra = ['Single Bomb'] * 2 + ['Single Arrow'] * 10 + ['Rupee (1)'] * 7 + ['Rupees (20)'] * 6 insanefinal25extra = ['Single Bomb'] * 2 + ['Single Arrow'] * 10 + ['Rupee (1)'] * 7 + ['Rupees (20)'] * 6
Difficulty = namedtuple('Difficulty', Difficulty = namedtuple('Difficulty',
@ -118,7 +121,7 @@ difficulties = {
timedohko = ['Green Clock'] * 25, timedohko = ['Green Clock'] * 25,
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 5, # +5 more Red Clocks if there is room timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 5, # +5 more Red Clocks if there is room
triforcehunt = ['Triforce Piece'] * 30, triforcehunt = ['Triforce Piece'] * 30,
triforce_pieces_required = 10, triforce_pieces_required = 20,
conditional_extras = easy_conditional_extras, conditional_extras = easy_conditional_extras,
extras = [easyextra, easyfirst15extra, easysecond10extra, easythird5extra, easyfinal25extra], extras = [easyextra, easyfirst15extra, easysecond10extra, easythird5extra, easyfinal25extra],
progressive_sword_limit = 4, progressive_sword_limit = 4,
@ -140,10 +143,10 @@ difficulties = {
basicsword = ['Master Sword', 'Master Sword', 'Tempered Sword'], basicsword = ['Master Sword', 'Master Sword', 'Tempered Sword'],
timedohko = ['Green Clock'] * 20, timedohko = ['Green Clock'] * 20,
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
triforcehunt = ['Triforce Piece'] * 40, triforcehunt = ['Triforce Piece'] * 30,
triforce_pieces_required = 30, triforce_pieces_required = 20,
conditional_extras = no_conditonal_extras, conditional_extras = no_conditonal_extras,
extras = [hardfirst20extra, hardsecond20extra, hardthird20extra, hardfinal20extra], extras = [hardfirst20extra, hardsecond10extra, hardthird10extra, hardfourth10extra, hardfinal20extra],
progressive_sword_limit = 3, progressive_sword_limit = 3,
progressive_shield_limit = 2, progressive_shield_limit = 2,
progressive_armor_limit = 1, progressive_armor_limit = 1,
@ -163,10 +166,10 @@ difficulties = {
basicsword = ['Fighter Sword', 'Master Sword', 'Master Sword'], basicsword = ['Fighter Sword', 'Master Sword', 'Master Sword'],
timedohko = ['Green Clock'] * 20 + ['Red Clock'] * 5, timedohko = ['Green Clock'] * 20 + ['Red Clock'] * 5,
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
triforcehunt = ['Triforce Piece'] * 40, triforcehunt = ['Triforce Piece'] * 30,
triforce_pieces_required = 40, triforce_pieces_required = 20,
conditional_extras = no_conditonal_extras, conditional_extras = no_conditonal_extras,
extras = [expertfirst15extra, expertsecond25extra, expertthird15extra, expertfinal25extra], extras = [expertfirst15extra, expertsecond15extra, expertthird10extra, expertfourth5extra, expertfinal25extra],
progressive_sword_limit = 2, progressive_sword_limit = 2,
progressive_shield_limit = 0, progressive_shield_limit = 0,
progressive_armor_limit = 0, progressive_armor_limit = 0,
@ -186,10 +189,10 @@ difficulties = {
basicsword = ['Fighter Sword', 'Master Sword', 'Master Sword'], basicsword = ['Fighter Sword', 'Master Sword', 'Master Sword'],
timedohko = ['Green Clock'] * 20 + ['Red Clock'] * 5, timedohko = ['Green Clock'] * 20 + ['Red Clock'] * 5,
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
triforcehunt = ['Triforce Piece'] * 50, triforcehunt = ['Triforce Piece'] * 30,
triforce_pieces_required = 50, triforce_pieces_required = 20,
conditional_extras = no_conditonal_extras, conditional_extras = no_conditonal_extras,
extras = [insanefirst15extra, insanesecond25extra, insanethird10extra, insanefourth15extra, insanefinal25extra], extras = [insanefirst15extra, insanesecond15extra, insanethird10extra, insanefourth5extra, insanefinal25extra],
progressive_sword_limit = 2, progressive_sword_limit = 2,
progressive_shield_limit = 0, progressive_shield_limit = 0,
progressive_armor_limit = 0, progressive_armor_limit = 0,
@ -213,11 +216,16 @@ def generate_itempool(world):
world.get_location('Agahnim 2').event = True world.get_location('Agahnim 2').event = True
# set up item pool # set up item pool
(pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon) = get_pool_core(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode) if world.custom:
(pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode, world.customitemarray)
world.rupoor_cost = min(world.customitemarray[67], 9999)
else:
(pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode)
world.itempool = ItemFactory(pool) world.itempool = ItemFactory(pool)
for (location, item) in placed_items: for (location, item) in placed_items:
world.push_item(location, ItemFactory(item), False) world.push_item(location, ItemFactory(item), False)
world.get_location(location).event = True world.get_location(location).event = True
world.lamps_needed_for_dark_rooms = lamps_needed_for_dark_rooms
if clock_mode is not None: if clock_mode is not None:
world.clock_mode = clock_mode world.clock_mode = clock_mode
if treasure_hunt_count is not None: if treasure_hunt_count is not None:
@ -230,10 +238,10 @@ def generate_itempool(world):
# logic has some branches where having 4 hearts is one possible requirement (of several alternatives) # logic has some branches where having 4 hearts is one possible requirement (of several alternatives)
# rather than making all hearts/heart pieces progression items (which slows down generation considerably) # rather than making all hearts/heart pieces progression items (which slows down generation considerably)
# We mark one random heart container as an advancement item (or 4 heart peices in expert mode) # We mark one random heart container as an advancement item (or 4 heart pieces in expert mode)
if world.difficulty in ['easy', 'normal', 'hard']: if world.difficulty in ['easy', 'normal', 'hard'] and not (world.custom and world.customitemarray[30] == 0):
[item for item in world.itempool if item.name == 'Boss Heart Container'][0].advancement = True [item for item in world.itempool if item.name == 'Boss Heart Container'][0].advancement = True
elif world.difficulty in ['expert']: elif world.difficulty in ['expert'] and not (world.custom and world.customitemarray[29] < 4):
adv_heart_pieces = [item for item in world.itempool if item.name == 'Piece of Heart'][0:4] adv_heart_pieces = [item for item in world.itempool if item.name == 'Piece of Heart'][0:4]
for hp in adv_heart_pieces: for hp in adv_heart_pieces:
hp.advancement = True hp.advancement = True
@ -244,14 +252,35 @@ def generate_itempool(world):
world.required_medallions = (mm_medallion, tr_medallion) world.required_medallions = (mm_medallion, tr_medallion)
# distribute crystals # distribute crystals
fill_prizes(world)
def fill_prizes(world, attempts=15):
crystals = ItemFactory(['Red Pendant', 'Blue Pendant', 'Green Pendant', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 7', 'Crystal 5', 'Crystal 6']) crystals = ItemFactory(['Red Pendant', 'Blue Pendant', 'Green Pendant', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 7', 'Crystal 5', 'Crystal 6'])
crystal_locations = [world.get_location('Turtle Rock - Prize'), world.get_location('Eastern Palace - Prize'), world.get_location('Desert Palace - Prize'), world.get_location('Tower of Hera - Prize'), world.get_location('Palace of Darkness - Prize'), crystal_locations = [world.get_location('Turtle Rock - Prize'), world.get_location('Eastern Palace - Prize'), world.get_location('Desert Palace - Prize'), world.get_location('Tower of Hera - Prize'), world.get_location('Palace of Darkness - Prize'),
world.get_location('Thieves Town - Prize'), world.get_location('Skull Woods - Prize'), world.get_location('Swamp Palace - Prize'), world.get_location('Ice Palace - Prize'), world.get_location('Thieves Town - Prize'), world.get_location('Skull Woods - Prize'), world.get_location('Swamp Palace - Prize'), world.get_location('Ice Palace - Prize'),
world.get_location('Misery Mire - Prize')] world.get_location('Misery Mire - Prize')]
placed_prizes = [loc.item.name for loc in crystal_locations if loc.item is not None]
unplaced_prizes = [crystal for crystal in crystals if crystal.name not in placed_prizes]
empty_crystal_locations = [loc for loc in crystal_locations if loc.item is None]
while attempts:
attempts -= 1
try:
prizepool = list(unplaced_prizes)
prize_locs = list(empty_crystal_locations)
random.shuffle(prizepool)
random.shuffle(prize_locs)
fill_restrictive(world, world.get_all_state(keys=True), prize_locs, prizepool)
except FillError:
logging.getLogger('').info("Failed to place dungeon prizes. Will retry %s more times", attempts)
for location in empty_crystal_locations:
location.item = None
continue
break
else:
raise FillError('Unable to place dungeon prizes')
random.shuffle(crystal_locations)
fill_restrictive(world, world.get_all_state(keys=True), crystal_locations, crystals)
def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode): def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode):
@ -271,8 +300,12 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode):
else: else:
pool.extend(basicgloves) pool.extend(basicgloves)
lamps_needed_for_dark_rooms = 1
if difficulty == 'easy':
lamps_needed_for_dark_rooms = 3
# insanity shuffle doesn't have fake LW/DW logic so for now guaranteed Mirror and Moon Pearl at the start # insanity shuffle doesn't have fake LW/DW logic so for now guaranteed Mirror and Moon Pearl at the start
if shuffle == 'insanity': if shuffle == 'insanity_legacy':
placed_items.append(('Link\'s House', 'Magic Mirror')) placed_items.append(('Link\'s House', 'Magic Mirror'))
placed_items.append(('Sanctuary', 'Moon Pearl')) placed_items.append(('Sanctuary', 'Moon Pearl'))
else: else:
@ -349,7 +382,149 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode):
if goal == 'pedestal': if goal == 'pedestal':
placed_items.append(('Master Sword Pedestal', 'Triforce')) placed_items.append(('Master Sword Pedestal', 'Triforce'))
return (pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon) return (pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms)
def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, customitemarray):
pool = []
placed_items = []
clock_mode = None
treasure_hunt_count = None
treasure_hunt_icon = None
# Correct for insanely oversized item counts and take initial steps to handle undersized pools.
for x in range(0, 64):
if customitemarray[x] > total_items_to_place:
customitemarray[x] = total_items_to_place
if customitemarray[66] > total_items_to_place:
customitemarray[66] = total_items_to_place
itemtotal = 0
for x in range(0, 65):
itemtotal = itemtotal + customitemarray[x]
itemtotal = itemtotal + customitemarray[66]
pool.extend(['Bow'] * customitemarray[0])
pool.extend(['Silver Arrows']* customitemarray[1])
pool.extend(['Blue Boomerang'] * customitemarray[2])
pool.extend(['Red Boomerang'] * customitemarray[3])
pool.extend(['Hookshot'] * customitemarray[4])
pool.extend(['Mushroom'] * customitemarray[5])
pool.extend(['Magic Powder'] * customitemarray[6])
pool.extend(['Fire Rod'] * customitemarray[7])
pool.extend(['Ice Rod'] * customitemarray[8])
pool.extend(['Bombos'] * customitemarray[9])
pool.extend(['Ether'] * customitemarray[10])
pool.extend(['Quake'] * customitemarray[11])
pool.extend(['Lamp'] * customitemarray[12])
pool.extend(['Hammer'] * customitemarray[13])
pool.extend(['Shovel'] * customitemarray[14])
pool.extend(['Ocarina'] * customitemarray[15])
pool.extend(['Bug Catching Net'] * customitemarray[16])
pool.extend(['Book of Mudora'] * customitemarray[17])
pool.extend(['Cane of Somaria'] * customitemarray[19])
pool.extend(['Cane of Byrna'] * customitemarray[20])
pool.extend(['Cape'] * customitemarray[21])
pool.extend(['Pegasus Boots'] * customitemarray[23])
pool.extend(['Power Glove'] * customitemarray[24])
pool.extend(['Titans Mitts'] * customitemarray[25])
pool.extend(['Progressive Glove'] * customitemarray[26])
pool.extend(['Flippers'] * customitemarray[27])
pool.extend(['Piece of Heart'] * customitemarray[29])
pool.extend(['Boss Heart Container'] * customitemarray[30])
pool.extend(['Sanctuary Heart Container'] * customitemarray[31])
pool.extend(['Master Sword'] * customitemarray[33])
pool.extend(['Tempered Sword'] * customitemarray[34])
pool.extend(['Golden Sword'] * customitemarray[35])
pool.extend(['Blue Shield'] * customitemarray[37])
pool.extend(['Red Shield'] * customitemarray[38])
pool.extend(['Mirror Shield'] * customitemarray[39])
pool.extend(['Progressive Shield'] * customitemarray[40])
pool.extend(['Blue Mail'] * customitemarray[41])
pool.extend(['Red Mail'] * customitemarray[42])
pool.extend(['Progressive Armor'] * customitemarray[43])
pool.extend(['Magic Upgrade (1/2)'] * customitemarray[44])
pool.extend(['Magic Upgrade (1/4)'] * customitemarray[45])
pool.extend(['Bomb Upgrade (+5)'] * customitemarray[46])
pool.extend(['Bomb Upgrade (+10)'] * customitemarray[47])
pool.extend(['Arrow Upgrade (+5)'] * customitemarray[48])
pool.extend(['Arrow Upgrade (+10)'] * customitemarray[49])
pool.extend(['Single Arrow'] * customitemarray[50])
pool.extend(['Arrows (10)'] * customitemarray[51])
pool.extend(['Single Bomb'] * customitemarray[52])
pool.extend(['Bombs (3)'] * customitemarray[53])
pool.extend(['Rupee (1)'] * customitemarray[54])
pool.extend(['Rupees (5)'] * customitemarray[55])
pool.extend(['Rupees (20)'] * customitemarray[56])
pool.extend(['Rupees (50)'] * customitemarray[57])
pool.extend(['Rupees (100)'] * customitemarray[58])
pool.extend(['Rupees (300)'] * customitemarray[59])
pool.extend(['Rupoor'] * customitemarray[60])
pool.extend(['Blue Clock'] * customitemarray[61])
pool.extend(['Green Clock'] * customitemarray[62])
pool.extend(['Red Clock'] * customitemarray[63])
pool.extend(['Triforce Piece'] * customitemarray[64])
pool.extend(['Triforce'] * customitemarray[66])
diff = difficulties[difficulty]
lamps_needed_for_dark_rooms = 1
if difficulty == 'easy':
lamps_needed_for_dark_rooms = customitemarray[12]
# expert+ difficulties produce the same contents for
# all bottles, since only one bottle is available
if diff.same_bottle:
thisbottle = random.choice(diff.bottles)
for _ in range(customitemarray[18]):
if not diff.same_bottle:
thisbottle = random.choice(diff.bottles)
pool.append(thisbottle)
if customitemarray[64] > 0 or customitemarray[65] > 0:
treasure_hunt_count = max(min(customitemarray[65], 99), 1) #To display, count must be between 1 and 99.
treasure_hunt_icon = 'Triforce Piece'
# Ensure game is always possible to complete here, force sufficient pieces if the player is unwilling.
if (customitemarray[64] < treasure_hunt_count) and (goal == 'triforcehunt') and (customitemarray[66] == 0):
extrapieces = treasure_hunt_count - customitemarray[64]
pool.extend(['Triforce Piece'] * extrapieces)
itemtotal = itemtotal + extrapieces
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 goal == 'pedestal':
placed_items.append(('Master Sword Pedestal', 'Triforce'))
itemtotal = itemtotal + 1
if mode == 'standard':
if progressive == 'off':
placed_items.append(('Link\'s Uncle', 'Fighter Sword'))
pool.extend(['Fighter Sword'] * max((customitemarray[32] - 1), 0))
pool.extend(['Progressive Sword'] * customitemarray[36])
else:
placed_items.append(('Link\'s Uncle', 'Progressive Sword'))
pool.extend(['Fighter Sword'] * customitemarray[32])
pool.extend(['Progressive Sword'] * max((customitemarray[36] - 1), 0))
else:
pool.extend(['Fighter Sword'] * customitemarray[32])
pool.extend(['Progressive Sword'] * customitemarray[36])
if shuffle == 'insanity_legacy':
placed_items.append(('Link\'s House', 'Magic Mirror'))
placed_items.append(('Sanctuary', 'Moon Pearl'))
pool.extend(['Magic Mirror'] * max((customitemarray[22] -1 ), 0))
pool.extend(['Moon Pearl'] * max((customitemarray[28] - 1), 0))
else:
pool.extend(['Magic Mirror'] * customitemarray[22])
pool.extend(['Moon Pearl'] * customitemarray[28])
if itemtotal < total_items_to_place:
pool.extend(['Nothing'] * (total_items_to_place - itemtotal))
return (pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms)
# A quick test to ensure all combinations generate the correct amount of items. # A quick test to ensure all combinations generate the correct amount of items.
def test(): def test():

View File

@ -72,7 +72,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 'You have\nchosen the\narcher cla
'Crystal 5': (True, False, 'Crystal', [0x04, 0x32, 0x64, 0x40, 0x6E, 0x06], None, None, None, None, None, None), 'Crystal 5': (True, False, 'Crystal', [0x04, 0x32, 0x64, 0x40, 0x6E, 0x06], None, None, None, None, None, None),
'Crystal 6': (True, False, 'Crystal', [0x01, 0x32, 0x64, 0x40, 0x6F, 0x06], None, None, None, None, None, None), 'Crystal 6': (True, False, 'Crystal', [0x01, 0x32, 0x64, 0x40, 0x6F, 0x06], None, None, None, None, None, None),
'Crystal 7': (True, False, 'Crystal', [0x08, 0x34, 0x64, 0x40, 0x7C, 0x06], None, None, None, None, None, None), 'Crystal 7': (True, False, 'Crystal', [0x08, 0x34, 0x64, 0x40, 0x7C, 0x06], None, None, None, None, None, None),
'Single Arrow': (False, False, None, 0x43, 'a lonely arrow\nsits here.', 'and the arrow', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrow', 'archer boy sews again'), 'Single Arrow': (False, False, None, 0x43, 'a lonely arrow\nsits here.', 'and the arrow', 'stick-collecting kid', 'sewing needle for sale', 'fungus for arrow', 'archer boy sews again'),
'Arrows (10)': (False, False, None, 0x44, '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'), 'Arrows (10)': (False, False, None, 0x44, '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'),
'Arrow Upgrade (+10)': (False, False, None, 0x54, '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 Upgrade (+10)': (False, False, None, 0x54, '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 Upgrade (+5)': (False, False, None, 0x53, '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 Upgrade (+5)': (False, False, None, 0x53, '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'),

56
Main.py
View File

@ -15,31 +15,31 @@ from Fill import distribute_items_cutoff, distribute_items_staleness, distribute
from ItemList import generate_itempool, difficulties from ItemList import generate_itempool, difficulties
from Utils import output_path from Utils import output_path
__version__ = '0.5.2.1-dev' __version__ = '0.6.0'
logic_hash = [85, 160, 173, 64, 16, 14, 97, 193, 219, 26, 11, 156, 198, 142, 213, 141, logic_hash = [26, 76, 4, 144, 72, 105, 234, 233, 12, 184, 95, 94, 100, 13, 15, 174,
55, 60, 32, 174, 77, 128, 147, 3, 1, 118, 74, 50, 243, 6, 251, 36, 186, 135, 130, 189, 246, 254, 123, 245, 85, 241, 101, 129, 70, 255, 55, 248,
194, 65, 217, 120, 94, 150, 108, 99, 222, 233, 96, 70, 225, 236, 103, 21, 43, 146, 23, 179, 243, 208, 230, 176, 9, 88, 239, 226, 222, 203, 244, 183,
241, 138, 144, 95, 164, 62, 183, 25, 203, 33, 240, 228, 224, 181, 176, 155, 205, 74, 44, 5, 122, 220, 206, 47, 221, 125, 138, 155, 98, 79, 238, 119,
247, 151, 140, 24, 221, 53, 83, 37, 71, 195, 188, 184, 90, 61, 13, 154, 30, 24, 159, 39, 253, 27, 33, 218, 62, 82, 200, 28, 141, 191, 93, 22,
57, 230, 179, 45, 23, 59, 238, 130, 121, 5, 165, 38, 216, 136, 199, 132, 192, 54, 227, 108, 48, 78, 242, 166, 60, 250, 75, 145, 49, 212, 41, 25,
255, 34, 212, 208, 227, 126, 226, 104, 98, 75, 166, 158, 40, 234, 111, 72, 127, 89, 178, 157, 19, 158, 177, 231, 207, 66, 172, 17, 133, 61, 109, 86,
58, 133, 157, 252, 192, 84, 152, 116, 177, 124, 190, 46, 214, 8, 10, 81, 57, 143, 142, 219, 148, 209, 181, 87, 163, 40, 81, 114, 240, 103, 31, 175,
244, 67, 182, 2, 0, 237, 145, 80, 7, 197, 137, 168, 102, 235, 204, 91, 237, 185, 18, 173, 168, 45, 216, 106, 161, 16, 151, 139, 104, 134, 110, 21,
69, 9, 100, 139, 54, 172, 232, 105, 162, 115, 242, 170, 169, 254, 20, 117, 32, 131, 118, 182, 215, 67, 3, 73, 171, 71, 150, 147, 223, 247, 42, 132,
180, 220, 191, 110, 93, 163, 223, 185, 211, 210, 39, 47, 114, 207, 73, 146, 107, 149, 232, 153, 10, 201, 156, 225, 116, 194, 187, 204, 46, 165, 124, 92,
112, 12, 78, 4, 88, 171, 106, 87, 127, 123, 41, 178, 43, 201, 202, 167, 7, 0, 251, 126, 162, 80, 90, 154, 252, 197, 188, 52, 137, 117, 198, 63,
35, 30, 122, 44, 209, 19, 249, 18, 113, 186, 49, 52, 161, 86, 200, 149, 167, 38, 136, 96, 58, 11, 1, 115, 229, 224, 37, 112, 170, 59, 68, 196,
218, 107, 29, 27, 135, 159, 66, 17, 131, 129, 76, 250, 15, 248, 82, 239, 36, 64, 91, 213, 14, 180, 190, 164, 8, 56, 214, 77, 202, 193, 97, 84,
68, 63, 143, 28, 153, 48, 101, 119, 51, 31, 215, 42, 187, 92, 109, 245, 152, 83, 236, 211, 20, 217, 2, 228, 140, 69, 121, 111, 113, 128, 210, 51,
22, 56, 89, 206, 148, 229, 175, 134, 189, 205, 79, 196, 246, 253, 231, 125] 53, 6, 235, 34, 102, 29, 120, 35, 50, 65, 160, 249, 99, 169, 199, 195]
def main(args, seed=None): def main(args, seed=None):
start = time.clock() start = time.clock()
# initialize the world # initialize the world
world = World(args.shuffle, args.logic, args.mode, args.difficulty, args.timer, args.progressive, args.goal, args.algorithm, not args.nodungeonitems, args.beatableonly, args.shuffleganon, args.quickswap, args.fastmenu, args.disablemusic, args.keysanity) world = World(args.shuffle, args.logic, args.mode, args.difficulty, args.timer, args.progressive, args.goal, args.algorithm, not args.nodungeonitems, args.beatableonly, args.shuffleganon, args.quickswap, args.fastmenu, args.disablemusic, args.keysanity, args.custom, args.customitemarray)
logger = logging.getLogger('') logger = logging.getLogger('')
if seed is None: if seed is None:
random.seed(None) random.seed(None)
@ -92,9 +92,10 @@ def main(args, seed=None):
elif args.algorithm == 'vt25': elif args.algorithm == 'vt25':
distribute_items_restrictive(world, 0) distribute_items_restrictive(world, 0)
elif args.algorithm == 'vt26': elif args.algorithm == 'vt26':
distribute_items_restrictive(world, random.randint(0, 15), shuffled_locations)
distribute_items_restrictive(world, gt_filler(world), shuffled_locations)
elif args.algorithm == 'balanced': elif args.algorithm == 'balanced':
distribute_items_restrictive(world, random.randint(0, 15)) distribute_items_restrictive(world, gt_filler(world))
logger.info('Calculating playthrough.') logger.info('Calculating playthrough.')
@ -117,7 +118,7 @@ def main(args, seed=None):
rom = JsonRom() rom = JsonRom()
else: else:
rom = LocalRom(args.rom) rom = LocalRom(args.rom)
patch_rom(world, rom, bytearray(logic_hash), args.heartbeep, sprite) patch_rom(world, rom, bytearray(logic_hash), args.heartbeep, args.heartcolor, sprite)
if args.jsonout: if args.jsonout:
print(json.dumps({'patch': rom.patches, 'spoiler': world.spoiler.to_json()})) print(json.dumps({'patch': rom.patches, 'spoiler': world.spoiler.to_json()}))
else: else:
@ -131,9 +132,14 @@ def main(args, seed=None):
return world return world
def gt_filler(world):
if world.goal == 'triforcehunt':
return random.randint(15, 50)
return random.randint(0, 15)
def copy_world(world): def copy_world(world):
# ToDo: Not good yet # ToDo: Not good yet
ret = World(world.shuffle, world.logic, world.mode, world.difficulty, world.timer, world.progressive, world.goal, world.algorithm, world.place_dungeon_items, world.check_beatable_only, world.shuffle_ganon, world.quickswap, world.fastmenu, world.disable_music, world.keysanity) ret = World(world.shuffle, world.logic, world.mode, world.difficulty, world.timer, world.progressive, world.goal, world.algorithm, world.place_dungeon_items, world.check_beatable_only, world.shuffle_ganon, world.quickswap, world.fastmenu, world.disable_music, world.keysanity, world.custom, world.customitemarray)
ret.required_medallions = list(world.required_medallions) ret.required_medallions = list(world.required_medallions)
ret.swamp_patch_required = world.swamp_patch_required ret.swamp_patch_required = world.swamp_patch_required
ret.ganon_at_pyramid = world.ganon_at_pyramid ret.ganon_at_pyramid = world.ganon_at_pyramid
@ -146,6 +152,8 @@ def copy_world(world):
ret.can_access_trock_eyebridge = world.can_access_trock_eyebridge ret.can_access_trock_eyebridge = world.can_access_trock_eyebridge
ret.can_take_damage = world.can_take_damage ret.can_take_damage = world.can_take_damage
ret.difficulty_requirements = world.difficulty_requirements ret.difficulty_requirements = world.difficulty_requirements
ret.fix_fake_world = world.fix_fake_world
ret.lamps_needed_for_dark_rooms = world.lamps_needed_for_dark_rooms
create_regions(ret) create_regions(ret)
create_dungeons(ret) create_dungeons(ret)
@ -153,6 +161,7 @@ def copy_world(world):
for region in world.regions: for region in world.regions:
copied_region = ret.get_region(region.name) copied_region = ret.get_region(region.name)
copied_region.is_light_world = region.is_light_world copied_region.is_light_world = region.is_light_world
copied_region.is_dark_world = region.is_dark_world
for entrance in region.entrances: for entrance in region.entrances:
ret.get_entrance(entrance.name).connect(copied_region) ret.get_entrance(entrance.name).connect(copied_region)
@ -285,7 +294,8 @@ def create_playthrough(world):
old_world.spoiler.paths = {location.name : get_path(state, location.parent_region) for sphere in collection_spheres for location in sphere} old_world.spoiler.paths = {location.name : get_path(state, location.parent_region) for sphere in collection_spheres for location in sphere}
if any(exit == 'Pyramid Fairy' for path in old_world.spoiler.paths.values() for (_, exit) in path): if any(exit == 'Pyramid Fairy' for path in old_world.spoiler.paths.values() for (_, exit) in path):
old_world.spoiler.paths['Big Bomb Shop'] = get_path(state, world.get_region('Big Bomb Shop')) old_world.spoiler.paths['Big Bomb Shop'] = get_path(state, world.get_region('Big Bomb Shop'))
print(world.seed) if any(exit == 'Swamp Palace Moat' for path in old_world.spoiler.paths.values() for (_, exit) in path) or 'Sunken Treasure' in old_world.required_locations:
old_world.spoiler.paths['Dam'] = get_path(state, world.get_region('Dam'))
# we can finally output our playthrough # we can finally output our playthrough
old_world.spoiler.playthrough = OrderedDict([(str(i + 1), {str(location): str(location.item) for location in sphere}) for i, sphere in enumerate(collection_spheres)]) old_world.spoiler.playthrough = OrderedDict([(str(i + 1), {str(location): str(location.item) for location in sphere}) for i, sphere in enumerate(collection_spheres)])

View File

@ -33,7 +33,7 @@ def main(args):
start_time = time.clock() start_time = time.clock()
# initialize the world # initialize the world
world = World('vanilla', 'noglitches', 'standard', 'normal', 'none', 'on', 'ganon', 'freshness', False, False, False, args.quickswap, args.fastmenu, args.disablemusic, False) world = World('vanilla', 'noglitches', 'standard', 'normal', 'none', 'on', 'ganon', 'freshness', False, False, False, args.quickswap, args.fastmenu, args.disablemusic, False, False, None)
logger = logging.getLogger('') logger = logging.getLogger('')
hasher = hashlib.md5() hasher = hashlib.md5()
@ -84,7 +84,7 @@ def main(args):
sprite = None sprite = None
rom = LocalRom(args.rom) rom = LocalRom(args.rom)
patch_rom(world, rom, logic_hash, args.heartbeep, sprite) patch_rom(world, rom, logic_hash, args.heartbeep, args.heartcolor, sprite)
for textname, texttype, text in text_patches: for textname, texttype, text in text_patches:
if texttype == 'text': if texttype == 'text':
@ -219,6 +219,8 @@ def start():
parser.add_argument('--disablemusic', help='Disables game music.', action='store_true') parser.add_argument('--disablemusic', help='Disables game music.', action='store_true')
parser.add_argument('--heartbeep', default='normal', const='normal', nargs='?', choices=['normal', 'half', 'quarter', 'off'], 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.') help='Select the rate at which the heart beep sound is played at low health.')
parser.add_argument('--heartcolor', default='red', const='red', nargs='?', choices=['red', 'blue', 'green', 'yellow'],
help='Select the color of Link\'s heart meter. (default: %(default)s)')
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('--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.') parser.add_argument('--plando', help='Filled out template to use for setting up the rom.')
args = parser.parse_args() args = parser.parse_args()

View File

@ -71,25 +71,20 @@ Ganon cannot be damaged until all dungeons (including Hyrule Castle Tower and Ga
### Triforce Hunt ### Triforce Hunt
Triforce Pieces are added to the item pool, and some number of them being found will trigger game completion. Ganon cannot be damaged. Triforce Pieces are added to the item pool, and some number of them being found will trigger game completion. Ganon cannot be damaged.
Counts are based on the difficulty setting as well as the required number. By default 30 Triforce Pieces are placed while 20 are needed to beat the game. Both values can be adjusted with the custom item pool feature.
Difficulty Need/Total
Easy 10/30
Normal 20/30
Hard 30/40
Expert 40/40
Insane 50/50
### Crystals ### Crystals
Standard game completion requiring you to collect the 7 crystals and then beat Ganon. Standard game completion requiring you to collect the 7 crystals and then beat Ganon.
This is only noticeably different if the --shuffleganon option is enabled. This is only noticeably different if the the Ganon shuffle option is enabled.
## Game Difficulty ## Game Difficulty
### Easy ### Easy
This setting doubles the number of swords, shields, armors, and bottles in the item pool. This setting doubles the number of swords, shields, armors, bottles, and silver arrows in the item pool.
This setting will also triple the number of Lamps available, and all will be obtainable before dark rooms.
Within dungeons, the number of items found will be displayed on screen if there is no timer. Within dungeons, the number of items found will be displayed on screen if there is no timer.
### Normal ### Normal
@ -204,32 +199,38 @@ staleness, decreasing the likelihood of receiving a progress item.
## Entrance Shuffle Algorithm ## Entrance Shuffle Algorithm
Determines how locations are shuffled. Determines how locations are shuffled. In all modes other than Insanity and the similar legacy versions, holes shuffle as a pair with the connecting cave and the front
two sections of Skull Woods remain confined to the general Skull Woods area. Link's house is never shuffled as a design decision.
### Default ### Vanilla
Is the Vanilla layout. Places entrances in the same locations they were in the original The Legend of Zelda: A Link to the Past.
### Simple ### Simple
Shuffles Dungeon Entrances/Exits between each other and keeps all 4-entrance dungeons confined to one location. Outside Light World Death Mountain, interiors are shuffled but still connect the same points Shuffles dungeon entrances between each other and keeps all 4-entrance dungeons confined to one location such that dungeons will one to one swap with each other.
on the overworld. On Death Mountain, entrances are connected more freely. Other than on Light World Death Mountain, interiors are shuffled but still connect the same points on the overworld. On Death Mountain, entrances are connected more freely.
### Full
Mixes cave and dungeon entrances freely.
### Restricted ### Restricted
Uses Dungeons shuffling from Simple but freely connects remaining entrances. Uses dungeon shuffling from Simple but freely connects remaining entrances. Caves and dungeons with multiple entrances will be confined to one world.
### Madness ### Full
Decouples entrances and exits from each other and shuffles them freely, only ensuring that no fake Light/Dark World happens and all locations are reachable. Mixes cave and dungeon entrances freely. Caves and dungeons with multiple entrances will be confined to one world.
### Crossed
Mixes cave and dungeon entrances freely, but now connector caves and dungeons can link Light World and Dark World.
### Insanity ### Insanity
Madness, but without the light/dark world restrictions. Gives access to Mirror and Moon Pearl from the start. Decouples entrances and exits from each other and shuffles them freely. Caves that were single entrance in vanilla still can only exit to the same location from which they were entered.
### Legacy Variants
Similar to the base shuffles, but the distinction between single entrance and multi-entrance caves from older versions of the randomizer is maintained.
Madness_Legacy is the more similar to the modern Insanity. Insanity_Legacy has fake worlds and guaranteed Moon Pearl and Magic Mirror for a very different experience.
### Dungeon Variants ### Dungeon Variants
@ -239,6 +240,10 @@ The dungeon variants only mix up dungeons and keep the rest of the overworld van
Select frequency of beeps when on low health. Can completely disable them. Select frequency of beeps when on low health. Can completely disable them.
## Heart Color
Select the color of Link's hearts.
## Menu Speed ## Menu Speed
A setting that lets the player set the rate at which the menu opens and closes. A setting that lets the player set the rate at which the menu opens and closes.
@ -275,7 +280,13 @@ If set, will only ensure the goal can be achieved, but not necessarily that all
## Include Ganon's Tower and Pyramid Hole in Shuffle pool ## Include Ganon's Tower and Pyramid Hole in Shuffle pool
If set, Ganon's Tower is included in the dungeon shuffle pool and the Pyramid Hole/Exit pair is included in the Holes shuffle pool. Ganon can not be defeated until the primary goal is fulfilled. This setting removes any bias against Ganon's Tower that some algorithms may have. If set, Ganon's Tower is included in the dungeon shuffle pool and the Pyramid Hole/Exit pair is included in the Holes shuffle pool. Ganon can not be defeated until the primary goal is fulfilled.
This setting removes any bias against Ganon's Tower that some algorithms may have.
## Use Custom Item Pool
If set, the item pool normally associated with your difficulty setting is replaced by the item pool specified in the custom tab. This feature is only supported when the randomizer is run
via the GUI; attempting to set this via the command line does nothing.
## Seed ## Seed
@ -422,10 +433,10 @@ Use to select a different sprite sheet to use for Link. Path to a binary file of
Enables the "Only Ensure Seed Beatable" option (default: False) Enables the "Only Ensure Seed Beatable" option (default: False)
``` ```
--shuffleganon --no-shuffleganon
``` ```
Enables the "Include Ganon's Tower and Pyramid Hole in Shuffle pool" option. (default: false) Disables the "Include Ganon's Tower and Pyramid Hole in Shuffle pool" option. (default: Enabled)
``` ```
--suppress_rom --suppress_rom

View File

@ -1,280 +1,311 @@
import collections import collections
from BaseClasses import Region, Location, Entrance from BaseClasses import Region, Location, Entrance, RegionType
def create_regions(world): def create_regions(world):
world.regions = [ world.regions = [
create_region('Light World', ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest'], create_lw_region('Light World', ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest'],
["Blinds Hideout", "Hyrule Castle Secret Entrance Drop", 'Zoras River', 'Kings Grave', 'Dam', ["Blinds Hideout", "Hyrule Castle Secret Entrance Drop", 'Zoras River', 'Kings Grave Outer Rocks', 'Dam',
'Links House', 'Tavern North', 'Chicken House', 'Aginahs Cave', 'Sahasrahlas Hut', 'Kakariko Well Drop', 'Kakariko Well Cave', 'Links House', 'Tavern North', 'Chicken House', 'Aginahs Cave', 'Sahasrahlas Hut', 'Kakariko Well Drop', 'Kakariko Well Cave',
'Blacksmiths Hut', 'Bat Cave Drop Ledge', 'Bat Cave Cave', 'Sick Kids House', 'Hobo Bridge', 'Lost Woods Hideout Drop', 'Lost Woods Hideout Stump', 'Blacksmiths Hut', 'Bat Cave Drop Ledge', 'Bat Cave Cave', 'Sick Kids House', 'Hobo Bridge', 'Lost Woods Hideout Drop', 'Lost Woods Hideout Stump',
'Lumberjack Tree Tree', 'Lumberjack Tree Cave', 'Mini Moldorm Cave', 'Ice Rod Cave', 'Lake Hylia Central Island Pier', 'Lumberjack Tree Tree', 'Lumberjack Tree Cave', 'Mini Moldorm Cave', 'Ice Rod Cave', 'Lake Hylia Central Island Pier',
'Bonk Rock Cave', 'Library', 'Potion Shop', 'Two Brothers House (East)', 'Desert Palace Stairs', 'Eastern Palace', 'Master Sword Meadow', 'Bonk Rock Cave', 'Library', 'Potion Shop', 'Two Brothers House (East)', 'Desert Palace Stairs', 'Eastern Palace', 'Master Sword Meadow',
'Sanctuary', 'Sanctuary Grave', 'Old Man Cave (West)', 'Flute Spot 1', 'Dark Desert Teleporter', 'East Hyrule Teleporter', 'South Hyrule Teleporter', 'Kakariko Teleporter', 'Sanctuary', 'Sanctuary Grave', 'Death Mountain Entrance Rock', 'Flute Spot 1', 'Dark Desert Teleporter', 'East Hyrule Teleporter', 'South Hyrule Teleporter', 'Kakariko Teleporter',
'Elder House (East)', 'Elder House (West)', 'North Fairy Cave', 'North Fairy Cave Drop', 'Lost Woods Gamble', 'Snitch Lady (East)', 'Snitch Lady (West)', 'Tavern (Front)', 'Elder House (East)', 'Elder House (West)', 'North Fairy Cave', 'North Fairy Cave Drop', 'Lost Woods Gamble', 'Snitch Lady (East)', 'Snitch Lady (West)', 'Tavern (Front)',
'Bush Covered House', 'Light World Bomb Hut', 'Kakariko Shop', 'Long Fairy Cave', 'Good Bee Cave', '20 Rupee Cave', 'Cave Shop (Lake Hylia)', 'Waterfall of Wishing', 'Hyrule Castle Main Gate', 'Bush Covered House', 'Light World Bomb Hut', 'Kakariko Shop', 'Long Fairy Cave', 'Good Bee Cave', '20 Rupee Cave', 'Cave Shop (Lake Hylia)', 'Waterfall of Wishing', 'Hyrule Castle Main Gate',
'Bonk Fairy (Light)', '50 Rupee Cave', 'Fortune Teller (Light)', 'Lake Hylia Fairy', 'Swamp Fairy', 'Desert Fairy', 'Lumberjack House', 'Lake Hylia Fortune Teller', 'Kakariko Gamble Game', 'Top of Pyramid']), 'Bonk Fairy (Light)', '50 Rupee Cave', 'Fortune Teller (Light)', 'Lake Hylia Fairy', 'Light Hype Fairy', 'Desert Fairy', 'Lumberjack House', 'Lake Hylia Fortune Teller', 'Kakariko Gamble Game', 'Top of Pyramid']),
create_region('Lake Hylia Central Island', None, ['Capacity Upgrade', 'Lake Hylia Central Island Teleporter']), create_lw_region('Death Mountain Entrance', None, ['Old Man Cave (West)', 'Death Mountain Entrance Drop']),
create_region('Blinds Hideout', ["Blind\'s Hideout - Top", create_lw_region('Lake Hylia Central Island', None, ['Capacity Upgrade', 'Lake Hylia Central Island Teleporter']),
"Blind\'s Hideout - Left", create_cave_region('Blinds Hideout', ["Blind\'s Hideout - Top",
"Blind\'s Hideout - Right", "Blind\'s Hideout - Left",
"Blind\'s Hideout - Far Left", "Blind\'s Hideout - Right",
"Blind\'s Hideout - Far Right"]), "Blind\'s Hideout - Far Left",
create_region('Hyrule Castle Secret Entrance', ['Link\'s Uncle', 'Secret Passage'], ['Hyrule Castle Secret Entrance Exit']), "Blind\'s Hideout - Far Right"]),
create_region('Zoras River', ['King Zora', 'Zora\'s Ledge']), create_cave_region('Hyrule Castle Secret Entrance', ['Link\'s Uncle', 'Secret Passage'], ['Hyrule Castle Secret Entrance Exit']),
create_region('Waterfall of Wishing', ['Waterfall Fairy - Left', 'Waterfall Fairy - Right']), create_lw_region('Zoras River', ['King Zora', 'Zora\'s Ledge']),
create_region('Kings Grave', ['King\'s Tomb']), create_cave_region('Waterfall of Wishing', ['Waterfall Fairy - Left', 'Waterfall Fairy - Right']),
create_region('North Fairy Cave', None, ['North Fairy Cave Exit']), create_lw_region('Kings Grave Area', None, ['Kings Grave', 'Kings Grave Inner Rocks']),
create_region('Dam', ['Floodgate Chest']), create_cave_region('Kings Grave', ['King\'s Tomb']),
create_region('Links House', ['Link\'s House'], ['Links House Exit']), create_cave_region('North Fairy Cave', None, ['North Fairy Cave Exit']),
create_region('Tavern', ['Kakariko Tavern']), create_cave_region('Dam', ['Floodgate Chest']),
create_region('Elder House', None, ['Elder House Exit (East)', 'Elder House Exit (West)']), create_cave_region('Links House', ['Link\'s House'], ['Links House Exit']),
create_region('Snitch Lady (East)'), create_cave_region('Chris Houlihan Room', None, ['Chris Houlihan Room Exit']),
create_region('Snitch Lady (West)'), create_cave_region('Tavern', ['Kakariko Tavern']),
create_region('Bush Covered House'), create_cave_region('Elder House', None, ['Elder House Exit (East)', 'Elder House Exit (West)']),
create_region('Tavern (Front)'), create_cave_region('Snitch Lady (East)'),
create_region('Light World Bomb Hut'), create_cave_region('Snitch Lady (West)'),
create_region('Kakariko Shop'), create_cave_region('Bush Covered House'),
create_region('Fortune Teller (Light)'), create_cave_region('Tavern (Front)'),
create_region('Lumberjack House'), create_cave_region('Light World Bomb Hut'),
create_region('Bonk Fairy'), # near links house both worlds create_cave_region('Kakariko Shop'),
create_region('Healer Fairy'), # 8 entrances? create_cave_region('Fortune Teller (Light)'),
create_region('Chicken House', ['Chicken House']), create_cave_region('Lake Hylia Fortune Teller'),
create_region('Aginahs Cave', ['Aginah\'s Cave']), create_cave_region('Lumberjack House'),
create_region('Sahasrahlas Hut', ['Sahasrahla\'s Hut - Left', 'Sahasrahla\'s Hut - Middle', 'Sahasrahla\'s Hut - Right', 'Sahasrahla']), create_cave_region('Bonk Fairy (Light)'),
create_region('Kakariko Well (top)', ['Kakariko Well - Top', 'Kakariko Well - Left', 'Kakariko Well - Middle', create_cave_region('Bonk Fairy (Dark)'),
'Kakariko Well - Right', 'Kakariko Well - Bottom'], ['Kakariko Well (top to bottom)']), create_cave_region('Lake Hylia Healer Fairy'),
create_region('Kakariko Well (bottom)', None, ['Kakariko Well Exit']), create_cave_region('Swamp Healer Fairy'),
create_region('Blacksmiths Hut', ['Blacksmith']), create_cave_region('Desert Healer Fairy'),
create_region('Bat Cave Drop Ledge', None, ['Bat Cave Drop']), create_cave_region('Dark Lake Hylia Healer Fairy'),
create_region('Bat Cave (right)', ['Magic Bat'], ['Bat Cave Door']), create_cave_region('Dark Lake Hylia Ledge Healer Fairy'),
create_region('Bat Cave (left)', None, ['Bat Cave Exit']), create_cave_region('Dark Desert Healer Fairy'),
create_region('Sick Kids House', ['Sick Kid']), create_cave_region('Dark Death Mountain Healer Fairy'),
create_region('Hobo Bridge', ['Hobo']), create_cave_region('Chicken House', ['Chicken House']),
create_region('Lost Woods Hideout (top)', ['Lost Woods Hideout'], ['Lost Woods Hideout (top to bottom)']), create_cave_region('Aginahs Cave', ['Aginah\'s Cave']),
create_region('Lost Woods Hideout (bottom)', None, ['Lost Woods Hideout Exit']), create_cave_region('Sahasrahlas Hut', ['Sahasrahla\'s Hut - Left', 'Sahasrahla\'s Hut - Middle', 'Sahasrahla\'s Hut - Right', 'Sahasrahla']),
create_region('Lumberjack Tree (top)', ['Lumberjack Tree'], ['Lumberjack Tree (top to bottom)']), create_cave_region('Kakariko Well (top)', ['Kakariko Well - Top', 'Kakariko Well - Left', 'Kakariko Well - Middle',
create_region('Lumberjack Tree (bottom)', None, ['Lumberjack Tree Exit']), 'Kakariko Well - Right', 'Kakariko Well - Bottom'], ['Kakariko Well (top to bottom)']),
create_region('Cave 45 Ledge', None, ['Cave 45']), create_cave_region('Kakariko Well (bottom)', None, ['Kakariko Well Exit']),
create_region('Cave 45', ['Cave 45']), create_cave_region('Blacksmiths Hut', ['Blacksmith']),
create_region('Graveyard Ledge', None, ['Graveyard Cave']), create_lw_region('Bat Cave Drop Ledge', None, ['Bat Cave Drop']),
create_region('Graveyard Cave', ['Graveyard Cave']), create_cave_region('Bat Cave (right)', ['Magic Bat'], ['Bat Cave Door']),
create_region('Checkerboard Cave', ['Checkerboard Cave']), create_cave_region('Bat Cave (left)', None, ['Bat Cave Exit']),
create_region('Long Fairy Cave'), create_cave_region('Sick Kids House', ['Sick Kid']),
create_region('Mini Moldorm Cave', ['Mini Moldorm Cave - Far Left', 'Mini Moldorm Cave - Left', 'Mini Moldorm Cave - Right', create_lw_region('Hobo Bridge', ['Hobo']),
'Mini Moldorm Cave - Far Right', 'Mini Moldorm Cave - Generous Guy']), create_cave_region('Lost Woods Hideout (top)', ['Lost Woods Hideout'], ['Lost Woods Hideout (top to bottom)']),
create_region('Ice Rod Cave', ['Ice Rod Cave']), create_cave_region('Lost Woods Hideout (bottom)', None, ['Lost Woods Hideout Exit']),
create_region('Good Bee Cave'), create_cave_region('Lumberjack Tree (top)', ['Lumberjack Tree'], ['Lumberjack Tree (top to bottom)']),
create_region('20 Rupee Cave'), create_cave_region('Lumberjack Tree (bottom)', None, ['Lumberjack Tree Exit']),
create_region('Cave Shop'), # two connectors in vanilla create_lw_region('Cave 45 Ledge', None, ['Cave 45']),
create_region('Bonk Rock Cave', ['Bonk Rock Cave']), create_cave_region('Cave 45', ['Cave 45']),
create_region('Library', ['Library']), create_lw_region('Graveyard Ledge', None, ['Graveyard Cave']),
create_region('Kakariko Gamble Game'), create_cave_region('Graveyard Cave', ['Graveyard Cave']),
create_region('Potion Shop', ['Potion Shop']), create_cave_region('Checkerboard Cave', ['Checkerboard Cave']),
create_region('Lake Hylia Island', ['Lake Hylia Island']), create_cave_region('Long Fairy Cave'),
create_region('Capacity Upgrade'), create_cave_region('Mini Moldorm Cave', ['Mini Moldorm Cave - Far Left', 'Mini Moldorm Cave - Left', 'Mini Moldorm Cave - Right',
create_region('Two Brothers House', None, ['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)']), 'Mini Moldorm Cave - Far Right', 'Mini Moldorm Cave - Generous Guy']),
create_region('Maze Race Ledge', ['Maze Race'], ['Two Brothers House (West)']), create_cave_region('Ice Rod Cave', ['Ice Rod Cave']),
create_region('50 Rupee Cave'), create_cave_region('Good Bee Cave'),
create_region('Desert Ledge', ['Desert Ledge'], ['Desert Palace Entrance (North) Rocks', 'Desert Palace Entrance (West)']), create_cave_region('20 Rupee Cave'),
create_region('Desert Ledge (Northeast)', None, ['Checkerboard Cave']), create_cave_region('Cave Shop (Lake Hylia)'),
create_region('Desert Palace Stairs', None, ['Desert Palace Entrance (South)']), create_cave_region('Cave Shop (Dark Death Mountain)'),
create_region('Desert Palace Lone Stairs', None, ['Desert Palace Stairs Drop', 'Desert Palace Entrance (East)']), create_cave_region('Bonk Rock Cave', ['Bonk Rock Cave']),
create_region('Desert Palace Entrance (North) Spot', None, ['Desert Palace Entrance (North)', 'Desert Ledge Return Rocks']), create_cave_region('Library', ['Library']),
create_region('Desert Palace Main', ['Desert Palace - Big Chest', 'Desert Palace - Torch', 'Desert Palace - Map Chest'], create_cave_region('Kakariko Gamble Game'),
['Desert Palace Exit (South)', 'Desert Palace Exit (West)', 'Desert Palace Exit (East)', 'Desert Palace East Wing']), create_cave_region('Potion Shop', ['Potion Shop']),
create_region('Desert Palace East', ['Desert Palace - Compass Chest', 'Desert Palace - Big Key Chest']), create_lw_region('Lake Hylia Island', ['Lake Hylia Island']),
create_region('Desert Palace North', ['Desert Palace - Lanmolas', 'Desert Palace - Prize'], ['Desert Palace Exit (North)']), create_cave_region('Capacity Upgrade'),
create_region('Eastern Palace', ['Eastern Palace - Compass Chest', 'Eastern Palace - Big Chest', 'Eastern Palace - Cannonball Chest', create_cave_region('Two Brothers House', None, ['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)']),
'Eastern Palace - Big Key Chest', 'Eastern Palace - Map Chest', 'Eastern Palace - Armos Knights', 'Eastern Palace - Prize'], ['Eastern Palace Exit']), create_lw_region('Maze Race Ledge', ['Maze Race'], ['Two Brothers House (West)']),
create_region('Master Sword Meadow', ['Master Sword Pedestal']), create_cave_region('50 Rupee Cave'),
create_region('Lost Woods Gamble'), create_lw_region('Desert Ledge', ['Desert Ledge'], ['Desert Palace Entrance (North) Rocks', 'Desert Palace Entrance (West)']),
create_region('Hyrule Castle Courtyard', None, ['Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Entrance (South)']), create_lw_region('Desert Ledge (Northeast)', None, ['Checkerboard Cave']),
create_region('Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Agahnims Tower', 'Hyrule Castle Ledge Courtyard Drop']), create_lw_region('Desert Palace Stairs', None, ['Desert Palace Entrance (South)']),
create_region('Hyrule Castle', ['Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest'], create_lw_region('Desert Palace Lone Stairs', None, ['Desert Palace Stairs Drop', 'Desert Palace Entrance (East)']),
['Hyrule Castle Exit (East)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (South)', 'Throne Room']), create_lw_region('Desert Palace Entrance (North) Spot', None, ['Desert Palace Entrance (North)', 'Desert Ledge Return Rocks']),
create_region('Sewer Drop', None, ['Sewer Drop']), # This exists only to be referenced for access checks create_dungeon_region('Desert Palace Main (Outer)', ['Desert Palace - Big Chest', 'Desert Palace - Torch', 'Desert Palace - Map Chest'],
create_region('Sewers (Dark)', ['Sewers - Dark Cross'], ['Sewers Door']), ['Desert Palace Pots (Outer)', 'Desert Palace Exit (West)', 'Desert Palace Exit (East)', 'Desert Palace East Wing']),
create_region('Sewers', ['Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle', create_dungeon_region('Desert Palace Main (Inner)', None, ['Desert Palace Exit (South)', 'Desert Palace Pots (Inner)']),
'Sewers - Secret Room - Right'], ['Sanctuary Push Door', 'Sewers Back Door']), create_dungeon_region('Desert Palace East', ['Desert Palace - Compass Chest', 'Desert Palace - Big Key Chest']),
create_region('Sanctuary', ['Sanctuary'], ['Sanctuary Exit']), create_dungeon_region('Desert Palace North', ['Desert Palace - Lanmolas', 'Desert Palace - Prize'], ['Desert Palace Exit (North)']),
create_region('Agahnims Tower', ['Castle Tower - Room 03', 'Castle Tower - Dark Maze'], ['Agahnim 1', 'Agahnims Tower Exit']), create_dungeon_region('Eastern Palace', ['Eastern Palace - Compass Chest', 'Eastern Palace - Big Chest', 'Eastern Palace - Cannonball Chest',
create_region('Agahnim 1', ['Agahnim 1'], None), 'Eastern Palace - Big Key Chest', 'Eastern Palace - Map Chest', 'Eastern Palace - Armos Knights', 'Eastern Palace - Prize'], ['Eastern Palace Exit']),
create_region('Old Man Cave', ['Old Man'], ['Old Man Cave Exit (East)', 'Old Man Cave Exit (West)']), create_lw_region('Master Sword Meadow', ['Master Sword Pedestal']),
create_region('Old Man House', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']), create_cave_region('Lost Woods Gamble'),
create_region('Old Man House Back', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']), create_lw_region('Hyrule Castle Courtyard', None, ['Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Entrance (South)']),
create_region('Death Mountain', None, ['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)', 'Death Mountain Teleporter']), create_lw_region('Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Agahnims Tower', 'Hyrule Castle Ledge Courtyard Drop']),
create_region('Death Mountain Return Cave', None, ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave Exit (East)']), create_dungeon_region('Hyrule Castle', ['Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest'],
create_region('Death Mountain Return Ledge', None, ['Death Mountain Return Ledge Drop', 'Death Mountain Return Cave (West)']), ['Hyrule Castle Exit (East)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (South)', 'Throne Room']),
create_region('Spectacle Rock Cave (Top)', ['Spectacle Rock Cave'], ['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']), create_dungeon_region('Sewer Drop', None, ['Sewer Drop']), # This exists only to be referenced for access checks
create_region('Spectacle Rock Cave (Bottom)', None, ['Spectacle Rock Cave Exit']), create_dungeon_region('Sewers (Dark)', ['Sewers - Dark Cross'], ['Sewers Door']),
create_region('Spectacle Rock Cave (Peak)', None, ['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']), create_dungeon_region('Sewers', ['Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle',
create_region('East Death Mountain (Bottom)', None, ['Broken Bridge (East)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)', 'East Death Mountain Teleporter', 'Hookshot Fairy', 'Fairy Ascension Rocks', 'Spiral Cave (Bottom)']), 'Sewers - Secret Room - Right'], ['Sanctuary Push Door', 'Sewers Back Door']),
create_region('Hookshot Fairy'), create_dungeon_region('Sanctuary', ['Sanctuary'], ['Sanctuary Exit']),
create_region('Paradox Cave Front', None, ['Paradox Cave Push Block Reverse', 'Paradox Cave Exit (Bottom)']), create_dungeon_region('Agahnims Tower', ['Castle Tower - Room 03', 'Castle Tower - Dark Maze'], ['Agahnim 1', 'Agahnims Tower Exit']),
create_region('Paradox Cave Chest Area', ['Paradox Cave Lower - Far Left', create_dungeon_region('Agahnim 1', ['Agahnim 1'], None),
'Paradox Cave Lower - Left', create_cave_region('Old Man Cave', ['Old Man'], ['Old Man Cave Exit (East)', 'Old Man Cave Exit (West)']),
'Paradox Cave Lower - Right', create_cave_region('Old Man House', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']),
'Paradox Cave Lower - Far Right', create_cave_region('Old Man House Back', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']),
'Paradox Cave Lower - Middle', create_lw_region('Death Mountain', None, ['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)', 'Death Mountain Teleporter']),
'Paradox Cave Upper - Left', create_cave_region('Death Mountain Return Cave', None, ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave Exit (East)']),
'Paradox Cave Upper - Right'], create_lw_region('Death Mountain Return Ledge', None, ['Death Mountain Return Ledge Drop', 'Death Mountain Return Cave (West)']),
['Paradox Cave Push Block', 'Paradox Cave Bomb Jump']), create_cave_region('Spectacle Rock Cave (Top)', ['Spectacle Rock Cave'], ['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']),
create_region('Paradox Cave', None, ['Paradox Cave Exit (Middle)', 'Paradox Cave Exit (Top)', 'Paradox Cave Drop']), create_cave_region('Spectacle Rock Cave (Bottom)', None, ['Spectacle Rock Cave Exit']),
create_region('East Death Mountain (Top)', None, ['Paradox Cave (Top)', 'Death Mountain (Top)', 'Spiral Cave Ledge Access', 'East Death Mountain Drop', 'Turtle Rock Teleporter', 'Fairy Ascension Ledge']), create_cave_region('Spectacle Rock Cave (Peak)', None, ['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']),
create_region('Spiral Cave Ledge', None, ['Spiral Cave', 'Spiral Cave Ledge Drop']), create_lw_region('East Death Mountain (Bottom)', None, ['Broken Bridge (East)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)', 'East Death Mountain Teleporter', 'Hookshot Fairy', 'Fairy Ascension Rocks', 'Spiral Cave (Bottom)']),
create_region('Spiral Cave (Top)', ['Spiral Cave'], ['Spiral Cave (top to bottom)', 'Spiral Cave Exit (Top)']), create_cave_region('Hookshot Fairy'),
create_region('Spiral Cave (Bottom)', None, ['Spiral Cave Exit']), create_cave_region('Paradox Cave Front', None, ['Paradox Cave Push Block Reverse', 'Paradox Cave Exit (Bottom)']),
create_region('Fairy Ascension Plateau', None, ['Fairy Ascension Drop', 'Fairy Ascension Cave (Bottom)']), create_cave_region('Paradox Cave Chest Area', ['Paradox Cave Lower - Far Left',
create_region('Fairy Ascension Cave', None, ['Fairy Ascension Cave Exit (Top)', 'Fairy Ascension Cave Exit (Bottom)']), 'Paradox Cave Lower - Left',
create_region('Fairy Ascension Ledge', None, ['Fairy Ascension Ledge Drop', 'Fairy Ascension Cave (Top)']), 'Paradox Cave Lower - Right',
create_region('Death Mountain (Top)', ['Ether Tablet'], ['East Death Mountain (Top)', 'Tower of Hera', 'Death Mountain Drop']), 'Paradox Cave Lower - Far Right',
create_region('Spectacle Rock', ['Spectacle Rock'], ['Spectacle Rock Drop']), 'Paradox Cave Lower - Middle',
create_region('Tower of Hera (Bottom)', ['Tower of Hera - Basement Cage', 'Tower of Hera - Map Chest'], ['Tower of Hera Small Key Door', 'Tower of Hera Big Key Door', 'Tower of Hera Exit']), 'Paradox Cave Upper - Left',
create_region('Tower of Hera (Basement)', ['Tower of Hera - Big Key Chest']), 'Paradox Cave Upper - Right'],
create_region('Tower of Hera (Top)', ['Tower of Hera - Compass Chest', 'Tower of Hera - Big Chest', 'Tower of Hera - Moldorm', 'Tower of Hera - Prize']), ['Paradox Cave Push Block', 'Paradox Cave Bomb Jump']),
create_cave_region('Paradox Cave', None, ['Paradox Cave Exit (Middle)', 'Paradox Cave Exit (Top)', 'Paradox Cave Drop']),
create_lw_region('East Death Mountain (Top)', None, ['Paradox Cave (Top)', 'Death Mountain (Top)', 'Spiral Cave Ledge Access', 'East Death Mountain Drop', 'Turtle Rock Teleporter', 'Fairy Ascension Ledge']),
create_lw_region('Spiral Cave Ledge', None, ['Spiral Cave', 'Spiral Cave Ledge Drop']),
create_cave_region('Spiral Cave (Top)', ['Spiral Cave'], ['Spiral Cave (top to bottom)', 'Spiral Cave Exit (Top)']),
create_cave_region('Spiral Cave (Bottom)', None, ['Spiral Cave Exit']),
create_lw_region('Fairy Ascension Plateau', None, ['Fairy Ascension Drop', 'Fairy Ascension Cave (Bottom)']),
create_cave_region('Fairy Ascension Cave', None, ['Fairy Ascension Cave Exit (Top)', 'Fairy Ascension Cave Exit (Bottom)']),
create_lw_region('Fairy Ascension Ledge', None, ['Fairy Ascension Ledge Drop', 'Fairy Ascension Cave (Top)']),
create_lw_region('Death Mountain (Top)', ['Ether Tablet'], ['East Death Mountain (Top)', 'Tower of Hera', 'Death Mountain Drop']),
create_lw_region('Spectacle Rock', ['Spectacle Rock'], ['Spectacle Rock Drop']),
create_dungeon_region('Tower of Hera (Bottom)', ['Tower of Hera - Basement Cage', 'Tower of Hera - Map Chest'], ['Tower of Hera Small Key Door', 'Tower of Hera Big Key Door', 'Tower of Hera Exit']),
create_dungeon_region('Tower of Hera (Basement)', ['Tower of Hera - Big Key Chest']),
create_dungeon_region('Tower of Hera (Top)', ['Tower of Hera - Compass Chest', 'Tower of Hera - Big Chest', 'Tower of Hera - Moldorm', 'Tower of Hera - Prize']),
create_region('East Dark World', ['Pyramid', 'Catfish'], ['Pyramid Fairy', 'South Dark World Bridge', 'West Dark World Gap', 'Palace of Darkness', 'Dark Lake Hylia Drop (East)', 'Dark Lake Hylia Teleporter', create_dw_region('East Dark World', ['Pyramid'], ['Pyramid Fairy', 'South Dark World Bridge', 'Palace of Darkness', 'Dark Lake Hylia Drop (East)', 'Dark Lake Hylia Teleporter',
'Hyrule Castle Ledge Mirror Spot', 'Dark Lake Hylia Fairy', 'Palace of Darkness Hint', 'East Dark World Hint', 'Dark World Potion Shop', 'Pyramid Hole']), 'Hyrule Castle Ledge Mirror Spot', 'Dark Lake Hylia Fairy', 'Palace of Darkness Hint', 'East Dark World Hint', 'Pyramid Hole', 'Northeast Dark World Broken Bridge Pass']),
create_region('Palace of Darkness Hint'), create_dw_region('Northeast Dark World', ['Catfish'], ['West Dark World Gap', 'Dark World Potion Shop', 'East Dark World Broken Bridge Pass']),
create_region('East Dark World Hint'), create_cave_region('Palace of Darkness Hint'),
create_region('South Dark World', ['Stumpy', 'Digging Game', 'Bombos Tablet'], ['Dark Lake Hylia Drop (South)', 'Hype Cave', 'Swamp Palace', 'Village of Outcasts Heavy Rock', create_cave_region('East Dark World Hint'),
'Maze Race Mirror Spot', 'Cave 45 Mirror Spot', 'East Dark World Bridge', 'Big Bomb Shop', 'Archery Game', 'Bonk Fairy (Dark)', 'Dark Lake Hylia Shop']), create_dw_region('South Dark World', ['Stumpy', 'Digging Game', 'Bombos Tablet'], ['Dark Lake Hylia Drop (South)', 'Hype Cave', 'Swamp Palace', 'Village of Outcasts Heavy Rock',
create_region('Big Bomb Shop'), 'Maze Race Mirror Spot', 'Cave 45 Mirror Spot', 'East Dark World Bridge', 'Big Bomb Shop', 'Archery Game', 'Bonk Fairy (Dark)', 'Dark Lake Hylia Shop']),
create_region('Archery Game'), create_cave_region('Big Bomb Shop'),
create_region('Dark Lake Hylia', None, ['Lake Hylia Island Mirror Spot', 'East Dark World Pier', 'Dark Lake Hylia Ledge']), create_cave_region('Archery Game'),
create_region('Dark Lake Hylia Central Island', None, ['Ice Palace', 'Lake Hylia Central Island Mirror Spot']), create_dw_region('Dark Lake Hylia', None, ['Lake Hylia Island Mirror Spot', 'East Dark World Pier', 'Dark Lake Hylia Ledge']),
create_region('Dark Lake Hylia Ledge', None, ['Dark Lake Hylia Ledge Drop', 'Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Spike Cave']), create_dw_region('Dark Lake Hylia Central Island', None, ['Ice Palace', 'Lake Hylia Central Island Mirror Spot']),
create_region('Dark Lake Hylia Ledge Hint'), create_dw_region('Dark Lake Hylia Ledge', None, ['Dark Lake Hylia Ledge Drop', 'Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Spike Cave']),
create_region('Dark Lake Hylia Ledge Spike Cave'), create_cave_region('Dark Lake Hylia Ledge Hint'),
create_region('Hype Cave', ['Hype Cave - Top', 'Hype Cave - Middle Right', 'Hype Cave - Middle Left', create_cave_region('Dark Lake Hylia Ledge Spike Cave'),
'Hype Cave - Bottom', 'Hype Cave - Generous Guy']), create_cave_region('Hype Cave', ['Hype Cave - Top', 'Hype Cave - Middle Right', 'Hype Cave - Middle Left',
create_region('West Dark World', None, ['Village of Outcasts Drop', 'East Dark World River Pier', 'Brewery', 'C-Shaped House', 'Chest Game', 'Thieves Town', 'Graveyard Ledge Mirror Spot', 'Bumper Cave (Bottom)', 'Skull Woods Forest', 'Hype Cave - Bottom', 'Hype Cave - Generous Guy']),
'Bat Cave Drop Ledge Mirror Spot', 'Dark World Hammer Peg Cave', 'Red Shield Shop', 'Dark Sanctuary Hint', 'Fortune Teller (Dark)', 'Dark World Shop', 'Dark World Lumberjack Shop']), create_dw_region('West Dark World', None, ['Village of Outcasts Drop', 'East Dark World River Pier', 'Brewery', 'C-Shaped House', 'Chest Game', 'Thieves Town', 'Graveyard Ledge Mirror Spot', 'Kings Grave Mirror Spot', 'Bumper Cave Entrance Rock',
create_region('Fortune Teller (Dark)'), 'Skull Woods Forest', 'Village of Outcasts Pegs', 'Village of Outcasts Eastern Rocks', 'Red Shield Shop', 'Dark Sanctuary Hint', 'Fortune Teller (Dark)', 'Dark World Lumberjack Shop']),
create_region('Dark World Shop'), create_dw_region('Dark Grassy Lawn', None, ['Grassy Lawn Pegs', 'Dark World Shop']),
create_region('Dark World Hammer Peg Cave', ['Peg Cave']), create_dw_region('Hammer Peg Area', None, ['Bat Cave Drop Ledge Mirror Spot', 'Dark World Hammer Peg Cave', 'Peg Area Rocks']),
create_region('Pyramid Fairy', ['Pyramid Fairy - Left', 'Pyramid Fairy - Right']), create_dw_region('Bumper Cave Entrance', None, ['Bumper Cave (Bottom)', 'Bumper Cave Entrance Mirror Spot', 'Bumper Cave Entrance Drop']),
create_region('Brewery', ['Brewery']), create_cave_region('Fortune Teller (Dark)'),
create_region('C-Shaped House', ['C-Shaped House']), create_cave_region('Village of Outcasts Shop'),
create_region('Chest Game', ['Chest Game']), create_cave_region('Dark Lake Hylia Shop'),
create_region('Red Shield Shop'), create_cave_region('Dark World Lumberjack Shop'),
create_region('Dark Sanctuary Hint'), create_cave_region('Dark World Potion Shop'),
create_region('Bumper Cave', None, ['Bumper Cave Exit (Bottom)', 'Bumper Cave Exit (Top)']), create_cave_region('Dark World Hammer Peg Cave', ['Peg Cave']),
create_region('Bumper Cave Ledge', ['Bumper Cave Ledge'], ['Bumper Cave Ledge Drop', 'Bumper Cave (Top)', 'Bumper Cave Ledge Mirror Spot']), create_cave_region('Pyramid Fairy', ['Pyramid Fairy - Left', 'Pyramid Fairy - Right']),
create_region('Skull Woods Forest', None, ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)', 'Skull Woods First Section Door', create_cave_region('Brewery', ['Brewery']),
'Skull Woods Second Section Door (East)']), create_cave_region('C-Shaped House', ['C-Shaped House']),
create_region('Skull Woods Forest (West)', None, ['Skull Woods Second Section Hole', 'Skull Woods Second Section Door (West)', 'Skull Woods Final Section']), create_cave_region('Chest Game', ['Chest Game']),
create_region('Dark Desert', None, ['Misery Mire', 'Mire Shed', 'Desert Ledge (Northeast) Mirror Spot', 'Desert Ledge Mirror Spot', 'Desert Palace Stairs Mirror Spot', 'Desert Palace Entrance (North) Mirror Spot', create_cave_region('Red Shield Shop'),
'Dark Desert Hint', 'Dark Desert Fairy']), create_cave_region('Dark Sanctuary Hint'),
create_region('Mire Shed', ['Mire Shed - Left', 'Mire Shed - Right']), create_cave_region('Bumper Cave', None, ['Bumper Cave Exit (Bottom)', 'Bumper Cave Exit (Top)']),
create_region('Dark Desert Hint'), create_dw_region('Bumper Cave Ledge', ['Bumper Cave Ledge'], ['Bumper Cave Ledge Drop', 'Bumper Cave (Top)', 'Bumper Cave Ledge Mirror Spot']),
create_region('Dark Death Mountain (West Bottom)', None, ['Spike Cave', 'Spectacle Rock Mirror Spot', 'Dark Death Mountain Fairy']), create_dw_region('Skull Woods Forest', None, ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)', 'Skull Woods First Section Door',
create_region('Dark Death Mountain (Top)', None, ['Dark Death Mountain Drop (East)', 'Dark Death Mountain Drop (West)', 'Ganons Tower', 'Superbunny Cave (Top)', 'Hookshot Cave', 'Skull Woods Second Section Door (East)']),
'East Death Mountain (Top) Mirror Spot', 'Turtle Rock']), create_dw_region('Skull Woods Forest (West)', None, ['Skull Woods Second Section Hole', 'Skull Woods Second Section Door (West)', 'Skull Woods Final Section']),
create_region('Dark Death Mountain Ledge', None, ['Dark Death Mountain Ledge (East)', 'Dark Death Mountain Ledge (West)', 'Mimic Cave Mirror Spot', 'Spiral Cave Mirror Spot']), create_dw_region('Dark Desert', None, ['Misery Mire', 'Mire Shed', 'Desert Ledge (Northeast) Mirror Spot', 'Desert Ledge Mirror Spot', 'Desert Palace Stairs Mirror Spot', 'Desert Palace Entrance (North) Mirror Spot',
create_region('Dark Death Mountain Isolated Ledge', None, ['Isolated Ledge Mirror Spot', 'Turtle Rock Isolated Ledge Entrance']), 'Dark Desert Hint', 'Dark Desert Fairy']),
create_region('Dark Death Mountain (East Bottom)', None, ['Superbunny Cave (Bottom)', 'Cave Shop (Dark Death Mountain)', 'Fairy Ascension Mirror Spot']), create_cave_region('Mire Shed', ['Mire Shed - Left', 'Mire Shed - Right']),
create_region('Superbunny Cave', ['Superbunny Cave - Top', 'Superbunny Cave - Bottom'], create_cave_region('Dark Desert Hint'),
['Superbunny Cave Exit (Top)', 'Superbunny Cave Exit (Bottom)']), create_dw_region('Dark Death Mountain (West Bottom)', None, ['Spike Cave', 'Spectacle Rock Mirror Spot', 'Dark Death Mountain Fairy']),
create_region('Spike Cave', ['Spike Cave']), create_dw_region('Dark Death Mountain (Top)', None, ['Dark Death Mountain Drop (East)', 'Dark Death Mountain Drop (West)', 'Ganons Tower', 'Superbunny Cave (Top)', 'Hookshot Cave',
create_region('Hookshot Cave', ['Hookshot Cave - Top Right', 'Hookshot Cave - Top Left', 'Hookshot Cave - Bottom Right', 'Hookshot Cave - Bottom Left'], 'East Death Mountain (Top) Mirror Spot', 'Turtle Rock']),
['Hookshot Cave Exit (South)', 'Hookshot Cave Exit (North)']), create_dw_region('Dark Death Mountain Ledge', None, ['Dark Death Mountain Ledge (East)', 'Dark Death Mountain Ledge (West)', 'Mimic Cave Mirror Spot', 'Spiral Cave Mirror Spot']),
create_region('Death Mountain Floating Island (Dark World)', None, ['Floating Island Drop', 'Hookshot Cave Back Entrance', 'Floating Island Mirror Spot']), create_dw_region('Dark Death Mountain Isolated Ledge', None, ['Isolated Ledge Mirror Spot', 'Turtle Rock Isolated Ledge Entrance']),
create_region('Death Mountain Floating Island (Light World)', ['Floating Island']), create_dw_region('Dark Death Mountain (East Bottom)', None, ['Superbunny Cave (Bottom)', 'Cave Shop (Dark Death Mountain)', 'Fairy Ascension Mirror Spot']),
create_region('Turtle Rock (Top)', None, ['Turtle Rock Drop']), create_cave_region('Superbunny Cave', ['Superbunny Cave - Top', 'Superbunny Cave - Bottom'],
create_region('Mimic Cave Ledge', None, ['Mimic Cave']), ['Superbunny Cave Exit (Top)', 'Superbunny Cave Exit (Bottom)']),
create_region('Mimic Cave', ['Mimic Cave']), create_cave_region('Spike Cave', ['Spike Cave']),
create_cave_region('Hookshot Cave', ['Hookshot Cave - Top Right', 'Hookshot Cave - Top Left', 'Hookshot Cave - Bottom Right', 'Hookshot Cave - Bottom Left'],
['Hookshot Cave Exit (South)', 'Hookshot Cave Exit (North)']),
create_dw_region('Death Mountain Floating Island (Dark World)', None, ['Floating Island Drop', 'Hookshot Cave Back Entrance', 'Floating Island Mirror Spot']),
create_lw_region('Death Mountain Floating Island (Light World)', ['Floating Island']),
create_dw_region('Turtle Rock (Top)', None, ['Turtle Rock Drop']),
create_lw_region('Mimic Cave Ledge', None, ['Mimic Cave']),
create_cave_region('Mimic Cave', ['Mimic Cave']),
create_region('Swamp Palace (Entrance)', None, ['Swamp Palace Moat', 'Swamp Palace Exit']), create_dungeon_region('Swamp Palace (Entrance)', None, ['Swamp Palace Moat', 'Swamp Palace Exit']),
create_region('Swamp Palace (First Room)', ['Swamp Palace - Entrance'], ['Swamp Palace Small Key Door']), create_dungeon_region('Swamp Palace (First Room)', ['Swamp Palace - Entrance'], ['Swamp Palace Small Key Door']),
create_region('Swamp Palace (Starting Area)', ['Swamp Palace - Map Chest'], ['Swamp Palace (Center)']), create_dungeon_region('Swamp Palace (Starting Area)', ['Swamp Palace - Map Chest'], ['Swamp Palace (Center)']),
create_region('Swamp Palace (Center)', ['Swamp Palace - Big Chest', 'Swamp Palace - Compass Chest', create_dungeon_region('Swamp Palace (Center)', ['Swamp Palace - Big Chest', 'Swamp Palace - Compass Chest',
'Swamp Palace - Big Key Chest', 'Swamp Palace - West Chest'], ['Swamp Palace (North)']), 'Swamp Palace - Big Key Chest', 'Swamp Palace - West Chest'], ['Swamp Palace (North)']),
create_region('Swamp Palace (North)', ['Swamp Palace - Flooded Room - Left', 'Swamp Palace - Flooded Room - Right', create_dungeon_region('Swamp Palace (North)', ['Swamp Palace - Flooded Room - Left', 'Swamp Palace - Flooded Room - Right',
'Swamp Palace - Waterfall Room', 'Swamp Palace - Arrghus', 'Swamp Palace - Prize']), 'Swamp Palace - Waterfall Room', 'Swamp Palace - Arrghus', 'Swamp Palace - Prize']),
create_region('Thieves Town (Entrance)', ['Thieves\' Town - Big Key Chest', create_dungeon_region('Thieves Town (Entrance)', ['Thieves\' Town - Big Key Chest',
'Thieves\' Town - Map Chest', 'Thieves\' Town - Map Chest',
'Thieves\' Town - Compass Chest', 'Thieves\' Town - Compass Chest',
'Thieves\' Town - Ambush Chest'], ['Thieves Town Big Key Door', 'Thieves Town Exit']), 'Thieves\' Town - Ambush Chest'], ['Thieves Town Big Key Door', 'Thieves Town Exit']),
create_region('Thieves Town (Deep)', ['Thieves\' Town - Attic', create_dungeon_region('Thieves Town (Deep)', ['Thieves\' Town - Attic',
'Thieves\' Town - Big Chest', 'Thieves\' Town - Big Chest',
'Thieves\' Town - Blind\'s Cell'], ['Blind Fight']), 'Thieves\' Town - Blind\'s Cell'], ['Blind Fight']),
create_region('Blind Fight', ['Thieves Town - Blind', 'Thieves Town - Prize']), create_dungeon_region('Blind Fight', ['Thieves Town - Blind', 'Thieves Town - Prize']),
create_region('Skull Woods First Section', ['Skull Woods - Map Chest'], ['Skull Woods First Section Exit', 'Skull Woods First Section Bomb Jump', 'Skull Woods First Section South Door', 'Skull Woods First Section West Door']), create_dungeon_region('Skull Woods First Section', ['Skull Woods - Map Chest'], ['Skull Woods First Section Exit', 'Skull Woods First Section Bomb Jump', 'Skull Woods First Section South Door', 'Skull Woods First Section West Door']),
create_region('Skull Woods First Section (Right)', ['Skull Woods - Pinball Room'], ['Skull Woods First Section (Right) North Door']), create_dungeon_region('Skull Woods First Section (Right)', ['Skull Woods - Pinball Room'], ['Skull Woods First Section (Right) North Door']),
create_region('Skull Woods First Section (Left)', ['Skull Woods - Compass Chest', 'Skull Woods - Pot Prison'], ['Skull Woods First Section (Left) Door to Exit', 'Skull Woods First Section (Left) Door to Right']), create_dungeon_region('Skull Woods First Section (Left)', ['Skull Woods - Compass Chest', 'Skull Woods - Pot Prison'], ['Skull Woods First Section (Left) Door to Exit', 'Skull Woods First Section (Left) Door to Right']),
create_region('Skull Woods First Section (Top)', ['Skull Woods - Big Chest'], ['Skull Woods First Section (Top) One-Way Path']), create_dungeon_region('Skull Woods First Section (Top)', ['Skull Woods - Big Chest'], ['Skull Woods First Section (Top) One-Way Path']),
create_region('Skull Woods Second Section (Drop)', None, ['Skull Woods Second Section (Drop)']), create_dungeon_region('Skull Woods Second Section (Drop)', None, ['Skull Woods Second Section (Drop)']),
create_region('Skull Woods Second Section', ['Skull Woods - Big Key Chest'], ['Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)']), create_dungeon_region('Skull Woods Second Section', ['Skull Woods - Big Key Chest'], ['Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)']),
create_region('Skull Woods Final Section (Entrance)', ['Skull Woods - Bridge Room'], ['Skull Woods Torch Room', 'Skull Woods Final Section Exit']), create_dungeon_region('Skull Woods Final Section (Entrance)', ['Skull Woods - Bridge Room'], ['Skull Woods Torch Room', 'Skull Woods Final Section Exit']),
create_region('Skull Woods Final Section (Mothula)', ['Skull Woods - Mothula', 'Skull Woods - Prize']), create_dungeon_region('Skull Woods Final Section (Mothula)', ['Skull Woods - Mothula', 'Skull Woods - Prize']),
create_region('Ice Palace (Entrance)', None, ['Ice Palace Entrance Room', 'Ice Palace Exit']), create_dungeon_region('Ice Palace (Entrance)', None, ['Ice Palace Entrance Room', 'Ice Palace Exit']),
create_region('Ice Palace (Main)', ['Ice Palace - Compass Chest', 'Ice Palace - Freezor Chest', create_dungeon_region('Ice Palace (Main)', ['Ice Palace - Compass Chest', 'Ice Palace - Freezor Chest',
'Ice Palace - Big Chest', 'Ice Palace - Iced T Room'], ['Ice Palace (East)', 'Ice Palace (Kholdstare)']), 'Ice Palace - Big Chest', 'Ice Palace - Iced T Room'], ['Ice Palace (East)', 'Ice Palace (Kholdstare)']),
create_region('Ice Palace (East)', ['Ice Palace - Spike Room'], ['Ice Palace (East Top)']), create_dungeon_region('Ice Palace (East)', ['Ice Palace - Spike Room'], ['Ice Palace (East Top)']),
create_region('Ice Palace (East Top)', ['Ice Palace - Big Key Chest', 'Ice Palace - Map Chest']), create_dungeon_region('Ice Palace (East Top)', ['Ice Palace - Big Key Chest', 'Ice Palace - Map Chest']),
create_region('Ice Palace (Kholdstare)', ['Ice Palace - Kholdstare', 'Ice Palace - Prize']), create_dungeon_region('Ice Palace (Kholdstare)', ['Ice Palace - Kholdstare', 'Ice Palace - Prize']),
create_region('Misery Mire (Entrance)', None, ['Misery Mire Entrance Gap', 'Misery Mire Exit']), create_dungeon_region('Misery Mire (Entrance)', None, ['Misery Mire Entrance Gap', 'Misery Mire Exit']),
create_region('Misery Mire (Main)', ['Misery Mire - Big Chest', 'Misery Mire - Map Chest', 'Misery Mire - Main Lobby', create_dungeon_region('Misery Mire (Main)', ['Misery Mire - Big Chest', 'Misery Mire - Map Chest', 'Misery Mire - Main Lobby',
'Misery Mire - Bridge Chest', 'Misery Mire - Spike Chest'], ['Misery Mire (West)', 'Misery Mire Big Key Door']), 'Misery Mire - Bridge Chest', 'Misery Mire - Spike Chest'], ['Misery Mire (West)', 'Misery Mire Big Key Door']),
create_region('Misery Mire (West)', ['Misery Mire - Compass Chest', 'Misery Mire - Big Key Chest']), create_dungeon_region('Misery Mire (West)', ['Misery Mire - Compass Chest', 'Misery Mire - Big Key Chest']),
create_region('Misery Mire (Final Area)', None, ['Misery Mire (Vitreous)']), create_dungeon_region('Misery Mire (Final Area)', None, ['Misery Mire (Vitreous)']),
create_region('Misery Mire (Vitreous)', ['Misery Mire - Vitreous', 'Misery Mire - Prize']), create_dungeon_region('Misery Mire (Vitreous)', ['Misery Mire - Vitreous', 'Misery Mire - Prize']),
create_region('Turtle Rock (Entrance)', None, ['Turtle Rock Entrance Gap', 'Turtle Rock Exit (Front)']), create_dungeon_region('Turtle Rock (Entrance)', None, ['Turtle Rock Entrance Gap', 'Turtle Rock Exit (Front)']),
create_region('Turtle Rock (First Section)', ['Turtle Rock - Compass Chest', 'Turtle Rock - Roller Room - Left', create_dungeon_region('Turtle Rock (First Section)', ['Turtle Rock - Compass Chest', 'Turtle Rock - Roller Room - Left',
'Turtle Rock - Roller Room - Right'], ['Turtle Rock Pokey Room', 'Turtle Rock Entrance Gap Reverse']), 'Turtle Rock - Roller Room - Right'], ['Turtle Rock Pokey Room', 'Turtle Rock Entrance Gap Reverse']),
create_region('Turtle Rock (Chain Chomp Room)', ['Turtle Rock - Chain Chomps'], ['Turtle Rock (Chain Chomp Room) (North)', 'Turtle Rock (Chain Chomp Room) (South)']), create_dungeon_region('Turtle Rock (Chain Chomp Room)', ['Turtle Rock - Chain Chomps'], ['Turtle Rock (Chain Chomp Room) (North)', 'Turtle Rock (Chain Chomp Room) (South)']),
create_region('Turtle Rock (Second Section)', ['Turtle Rock - Big Key Chest'], ['Turtle Rock Ledge Exit (West)', 'Turtle Rock Chain Chomp Staircase', 'Turtle Rock Big Key Door']), create_dungeon_region('Turtle Rock (Second Section)', ['Turtle Rock - Big Key Chest'], ['Turtle Rock Ledge Exit (West)', 'Turtle Rock Chain Chomp Staircase', 'Turtle Rock Big Key Door']),
create_region('Turtle Rock (Big Chest)', ['Turtle Rock - Big Chest'], ['Turtle Rock (Big Chest) (North)', 'Turtle Rock Ledge Exit (East)']), create_dungeon_region('Turtle Rock (Big Chest)', ['Turtle Rock - Big Chest'], ['Turtle Rock (Big Chest) (North)', 'Turtle Rock Ledge Exit (East)']),
create_region('Turtle Rock (Crystaroller Room)', ['Turtle Rock - Crystaroller Room'], ['Turtle Rock Dark Room Staircase', 'Turtle Rock Big Key Door Reverse']), create_dungeon_region('Turtle Rock (Crystaroller Room)', ['Turtle Rock - Crystaroller Room'], ['Turtle Rock Dark Room Staircase', 'Turtle Rock Big Key Door Reverse']),
create_region('Turtle Rock (Dark Room)', None, ['Turtle Rock (Dark Room) (North)', 'Turtle Rock (Dark Room) (South)']), create_dungeon_region('Turtle Rock (Dark Room)', None, ['Turtle Rock (Dark Room) (North)', 'Turtle Rock (Dark Room) (South)']),
create_region('Turtle Rock (Eye Bridge)', ['Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right', create_dungeon_region('Turtle Rock (Eye Bridge)', ['Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right',
'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'], 'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'],
['Turtle Rock Dark Room (South)', 'Turtle Rock (Trinexx)', 'Turtle Rock Isolated Ledge Exit']), ['Turtle Rock Dark Room (South)', 'Turtle Rock (Trinexx)', 'Turtle Rock Isolated Ledge Exit']),
create_region('Turtle Rock (Trinexx)', ['Turtle Rock - Trinexx', 'Turtle Rock - Prize']), create_dungeon_region('Turtle Rock (Trinexx)', ['Turtle Rock - Trinexx', 'Turtle Rock - Prize']),
create_region('Palace of Darkness (Entrance)', ['Palace of Darkness - Shooter Room'], ['Palace of Darkness Bridge Room', 'Palace of Darkness Bonk Wall', 'Palace of Darkness Exit']), create_dungeon_region('Palace of Darkness (Entrance)', ['Palace of Darkness - Shooter Room'], ['Palace of Darkness Bridge Room', 'Palace of Darkness Bonk Wall', 'Palace of Darkness Exit']),
create_region('Palace of Darkness (Center)', ['Palace of Darkness - The Arena - Bridge', 'Palace of Darkness - Stalfos Basement'], create_dungeon_region('Palace of Darkness (Center)', ['Palace of Darkness - The Arena - Bridge', 'Palace of Darkness - Stalfos Basement'],
['Palace of Darkness Big Key Chest Staircase', 'Palace of Darkness (North)', 'Palace of Darkness Big Key Door']), ['Palace of Darkness Big Key Chest Staircase', 'Palace of Darkness (North)', 'Palace of Darkness Big Key Door']),
create_region('Palace of Darkness (Big Key Chest)', ['Palace of Darkness - Big Key Chest']), create_dungeon_region('Palace of Darkness (Big Key Chest)', ['Palace of Darkness - Big Key Chest']),
create_region('Palace of Darkness (Bonk Section)', ['Palace of Darkness - The Arena - Ledge', 'Palace of Darkness - Map Chest'], ['Palace of Darkness Hammer Peg Drop']), create_dungeon_region('Palace of Darkness (Bonk Section)', ['Palace of Darkness - The Arena - Ledge', 'Palace of Darkness - Map Chest'], ['Palace of Darkness Hammer Peg Drop']),
create_region('Palace of Darkness (North)', ['Palace of Darkness - Compass Chest', 'Palace of Darkness - Dark Basement - Left', 'Palace of Darkness - Dark Basement - Right'], create_dungeon_region('Palace of Darkness (North)', ['Palace of Darkness - Compass Chest', 'Palace of Darkness - Dark Basement - Left', 'Palace of Darkness - Dark Basement - Right'],
['Palace of Darkness Spike Statue Room Door', 'Palace of Darkness Maze Door']), ['Palace of Darkness Spike Statue Room Door', 'Palace of Darkness Maze Door']),
create_region('Palace of Darkness (Maze)', ['Palace of Darkness - Dark Maze - Top', 'Palace of Darkness - Dark Maze - Bottom', 'Palace of Darkness - Big Chest']), create_dungeon_region('Palace of Darkness (Maze)', ['Palace of Darkness - Dark Maze - Top', 'Palace of Darkness - Dark Maze - Bottom', 'Palace of Darkness - Big Chest']),
create_region('Palace of Darkness (Harmless Hellway)', ['Palace of Darkness - Harmless Hellway']), create_dungeon_region('Palace of Darkness (Harmless Hellway)', ['Palace of Darkness - Harmless Hellway']),
create_region('Palace of Darkness (Final Section)', ['Palace of Darkness - Helmasaur', 'Palace of Darkness - Prize']), create_dungeon_region('Palace of Darkness (Final Section)', ['Palace of Darkness - Helmasaur', 'Palace of Darkness - Prize']),
create_region('Ganons Tower (Entrance)', ['Ganons Tower - Bob\'s Torch', 'Ganons Tower - Hope Room - Left', 'Ganons Tower - Hope Room - Right'], create_dungeon_region('Ganons Tower (Entrance)', ['Ganons Tower - Bob\'s Torch', 'Ganons Tower - Hope Room - Left', 'Ganons Tower - Hope Room - Right'],
['Ganons Tower (Tile Room)', 'Ganons Tower (Hookshot Room)', 'Ganons Tower Big Key Door', 'Ganons Tower Exit']), ['Ganons Tower (Tile Room)', 'Ganons Tower (Hookshot Room)', 'Ganons Tower Big Key Door', 'Ganons Tower Exit']),
create_region('Ganons Tower (Tile Room)', ['Ganons Tower - Tile Room'], ['Ganons Tower (Tile Room) Key Door']), create_dungeon_region('Ganons Tower (Tile Room)', ['Ganons Tower - Tile Room'], ['Ganons Tower (Tile Room) Key Door']),
create_region('Ganons Tower (Compass Room)', ['Ganons Tower - Compass Room - Top Left', 'Ganons Tower - Compass Room - Top Right', create_dungeon_region('Ganons Tower (Compass Room)', ['Ganons Tower - Compass Room - Top Left', 'Ganons Tower - Compass Room - Top Right',
'Ganons Tower - Compass Room - Bottom Left', 'Ganons Tower - Compass Room - Bottom Right'], 'Ganons Tower - Compass Room - Bottom Left', 'Ganons Tower - Compass Room - Bottom Right'],
['Ganons Tower (Bottom) (East)']), ['Ganons Tower (Bottom) (East)']),
create_region('Ganons Tower (Hookshot Room)', ['Ganons Tower - DMs Room - Top Left', 'Ganons Tower - DMs Room - Top Right', create_dungeon_region('Ganons Tower (Hookshot Room)', ['Ganons Tower - DMs Room - Top Left', 'Ganons Tower - DMs Room - Top Right',
'Ganons Tower - DMs Room - Bottom Left', 'Ganons Tower - DMs Room - Bottom Right'], 'Ganons Tower - DMs Room - Bottom Left', 'Ganons Tower - DMs Room - Bottom Right'],
['Ganons Tower (Map Room)', 'Ganons Tower (Double Switch Room)']), ['Ganons Tower (Map Room)', 'Ganons Tower (Double Switch Room)']),
create_region('Ganons Tower (Map Room)', ['Ganons Tower - Map Chest']), create_dungeon_region('Ganons Tower (Map Room)', ['Ganons Tower - Map Chest']),
create_region('Ganons Tower (Firesnake Room)', ['Ganons Tower - Firesnake Room'], ['Ganons Tower (Firesnake Room)']), create_dungeon_region('Ganons Tower (Firesnake Room)', ['Ganons Tower - Firesnake Room'], ['Ganons Tower (Firesnake Room)']),
create_region('Ganons Tower (Teleport Room)', ['Ganons Tower - Randomizer Room - Top Left', 'Ganons Tower - Randomizer Room - Top Right', create_dungeon_region('Ganons Tower (Teleport Room)', ['Ganons Tower - Randomizer Room - Top Left', 'Ganons Tower - Randomizer Room - Top Right',
'Ganons Tower - Randomizer Room - Bottom Left', 'Ganons Tower - Randomizer Room - Bottom Right'], 'Ganons Tower - Randomizer Room - Bottom Left', 'Ganons Tower - Randomizer Room - Bottom Right'],
['Ganons Tower (Bottom) (West)']), ['Ganons Tower (Bottom) (West)']),
create_region('Ganons Tower (Bottom)', ['Ganons Tower - Bob\'s Chest', 'Ganons Tower - Big Chest', 'Ganons Tower - Big Key Room - Left', create_dungeon_region('Ganons Tower (Bottom)', ['Ganons Tower - Bob\'s Chest', 'Ganons Tower - Big Chest', 'Ganons Tower - Big Key Room - Left',
'Ganons Tower - Big Key Room - Right', 'Ganons Tower - Big Key Chest']), 'Ganons Tower - Big Key Room - Right', 'Ganons Tower - Big Key Chest']),
create_region('Ganons Tower (Top)', None, ['Ganons Tower Torch Rooms']), create_dungeon_region('Ganons Tower (Top)', None, ['Ganons Tower Torch Rooms']),
create_region('Ganons Tower (Before Moldorm)', ['Ganons Tower - Mini Helmasaur Room - Left', 'Ganons Tower - Mini Helmasaur Room - Right', create_dungeon_region('Ganons Tower (Before Moldorm)', ['Ganons Tower - Mini Helmasaur Room - Left', 'Ganons Tower - Mini Helmasaur Room - Right',
'Ganons Tower - Pre-Moldorm Chest'], ['Ganons Tower Moldorm Door']), 'Ganons Tower - Pre-Moldorm Chest'], ['Ganons Tower Moldorm Door']),
create_region('Ganons Tower (Moldorm)', None, ['Ganons Tower Moldorm Gap']), create_dungeon_region('Ganons Tower (Moldorm)', None, ['Ganons Tower Moldorm Gap']),
create_region('Agahnim 2', ['Ganons Tower - Validation Chest', 'Agahnim 2'], None), create_dungeon_region('Agahnim 2', ['Ganons Tower - Validation Chest', 'Agahnim 2'], None),
create_region('Pyramid', ['Ganon'], ['Ganon Drop']), create_cave_region('Pyramid', ['Ganon'], ['Ganon Drop']),
create_region('Bottom of Pyramid', None, ['Pyramid Exit']), create_cave_region('Bottom of Pyramid', None, ['Pyramid Exit']),
create_region('Pyramid Ledge', None, ['Pyramid Entrance', 'Pyramid Drop']) create_dw_region('Pyramid Ledge', None, ['Pyramid Entrance', 'Pyramid Drop'])
] ]
world.intialize_regions() world.intialize_regions()
def create_lw_region(name, locations=None, exits=None):
return _create_region(name, RegionType.LightWorld, locations, exits)
def create_region(name, locations=None, exits=None): def create_dw_region(name, locations=None, exits=None):
ret = Region(name) return _create_region(name, RegionType.DarkWorld, locations, exits)
def create_cave_region(name, locations=None, exits=None):
return _create_region(name, RegionType.Cave, locations, exits)
def create_dungeon_region(name, locations=None, exits=None):
return _create_region(name, RegionType.Dungeon, locations, exits)
def _create_region(name, type, locations=None, exits=None):
ret = Region(name, type)
if locations is None: if locations is None:
locations = [] locations = []
if exits is None: if exits is None:
@ -287,22 +318,30 @@ def create_region(name, locations=None, exits=None):
ret.locations.append(Location(location, address, crystal, hint_text, ret)) ret.locations.append(Location(location, address, crystal, hint_text, ret))
return ret return ret
def mark_light_world_regions(world): def mark_light_world_regions(world):
# Note that in "inanity" shuffle this code may mark some dark world locations as being in light world. That is fine because this flag # cross world caves may have some sections marked as both in_light_world, and in_dark_work.
# is only used for bunny logic, and you start with a Moon pearl immediately availible in Insanity shuffle. # That is ok. the bunny logic will check for this case and incorporate special rules.
queue = collections.deque(region for region in world.regions if region.type == RegionType.LightWorld)
# Exclude entrances that represent connections from the light world to the dark world
excluded_entrances = set(['Top of Pyramid', 'Lake Hylia Central Island Teleporter', 'Dark Desert Teleporter', 'East Hyrule Teleporter', 'South Hyrule Teleporter', 'Kakariko Teleporter', 'Death Mountain Teleporter', 'East Death Mountain Teleporter', 'Turtle Rock Teleporter'])
starting_regions = ['Links House', 'Cave 45 Ledge', 'Graveyard Ledge', 'Mimic Cave Ledge', 'Death Mountain Floating Island (Light World)', 'Desert Ledge', 'Desert Ledge (Northeast)', 'Lake Hylia Island', 'Spectacle Rock', 'Death Mountain Return Ledge', 'Hyrule Castle Ledge','Maze Race Ledge']
queue = collections.deque([world.get_region(region) for region in starting_regions])
seen = set(queue) seen = set(queue)
while queue: while queue:
current = queue.popleft() current = queue.popleft()
current.is_light_world = True current.is_light_world = True
for exit in current.exits: for exit in current.exits:
if exit.name in excluded_entrances: if exit.connected_region.type == RegionType.DarkWorld:
# Don't venture into the dark world
continue
if exit.connected_region not in seen:
seen.add(exit.connected_region)
queue.append(exit.connected_region)
queue = collections.deque(region for region in world.regions if region.type == RegionType.DarkWorld)
seen = set(queue)
while queue:
current = queue.popleft()
current.is_dark_world = True
for exit in current.exits:
if exit.connected_region.type == RegionType.LightWorld:
# Don't venture into the light world
continue continue
if exit.connected_region not in seen: if exit.connected_region not in seen:
seen.add(exit.connected_region) seen.add(exit.connected_region)

313
Rom.py
View File

@ -15,7 +15,7 @@ from Items import ItemFactory
JAP10HASH = '03a63945398191337e896e5771f77173' JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = '214e4b2a50cb65cd13a8194bc88cb030' RANDOMIZERBASEHASH = 'dc5840f0d1ef7b51009c5625a054b3dd'
class JsonRom(object): class JsonRom(object):
@ -44,7 +44,7 @@ class LocalRom(object):
def __init__(self, file, patch=True): def __init__(self, file, patch=True):
with open(file, 'rb') as stream: with open(file, 'rb') as stream:
self.buffer = bytearray(stream.read()) self.buffer = read_rom(stream)
if patch: if patch:
self.patch_base_rom() self.patch_base_rom()
@ -94,6 +94,13 @@ class LocalRom(object):
inv = crc ^ 0xFFFF inv = crc ^ 0xFFFF
self.write_bytes(0x7FDC, [inv & 0xFF, (inv >> 8) & 0xFF, crc & 0xFF, (crc >> 8) & 0xFF]) self.write_bytes(0x7FDC, [inv & 0xFF, (inv >> 8) & 0xFF, crc & 0xFF, (crc >> 8) & 0xFF])
def read_rom(stream):
"Reads rom into bytearray and strips off any smc header"
buffer = bytearray(stream.read())
if len(buffer)%0x400 == 0x200:
buffer = buffer[0x200:]
return buffer
class Sprite(object): class Sprite(object):
default_palette = [255, 127, 126, 35, 183, 17, 158, 54, 165, 20, 255, 1, 120, 16, 157, default_palette = [255, 127, 126, 35, 183, 17, 158, 54, 165, 20, 255, 1, 120, 16, 157,
89, 71, 54, 104, 59, 74, 10, 239, 18, 92, 42, 113, 21, 24, 122, 89, 71, 54, 104, 59, 74, 10, 239, 18, 92, 42, 113, 21, 24, 122,
@ -261,7 +268,7 @@ def int32_as_bytes(value):
value = value & 0xFFFFFFFF value = value & 0xFFFFFFFF
return [value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF, (value >> 24) & 0xFF] return [value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF, (value >> 24) & 0xFF]
def patch_rom(world, rom, hashtable, beep='normal', sprite=None): def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None):
# patch items # patch items
for location in world.get_locations(): for location in world.get_locations():
itemid = location.item.code if location.item is not None else 0x5A itemid = location.item.code if location.item is not None else 0x5A
@ -297,13 +304,57 @@ def patch_rom(world, rom, hashtable, beep='normal', sprite=None):
if world.keysanity: if world.keysanity:
rom.write_byte(0x155C9, random.choice([0x11, 0x16])) # Randomize GT music too in keysanity mode rom.write_byte(0x155C9, random.choice([0x11, 0x16])) # Randomize GT music too in keysanity mode
# patch entrances # patch entrance/exits/holes
for region in world.regions: for region in world.regions:
for exit in region.exits: for exit in region.exits:
if exit.target is not None: if exit.target is not None:
addresses = [exit.addresses] if isinstance(exit.addresses, int) else exit.addresses if isinstance(exit.addresses, tuple):
for address in addresses: offset = exit.target
rom.write_byte(address, exit.target) room_id, ow_area, vram_loc, scroll_y, scroll_x, link_y, link_x, camera_y, camera_x, unknown_1, unknown_2, door_1, door_2 = exit.addresses
#room id is deliberately not written
rom.write_byte(0x15B8C + offset, ow_area)
rom.write_int16_to_rom(0x15BDB + 2 * offset, vram_loc)
rom.write_int16_to_rom(0x15C79 + 2 * offset, scroll_y)
rom.write_int16_to_rom(0x15D17 + 2 * offset, scroll_x)
# for positioning fixups we abuse the roomid as a way of identifying which exit data we are appling
# Thanks to Zarby89 for originally finding these values
# todo fix screen scrolling
if world.shuffle not in ['insanity', 'insanity_legacy', 'madness_legacy'] and \
exit.name in ['Eastern Palace Exit', 'Tower of Hera Exit', 'Thieves Town Exit', 'Skull Woods Final Section Exit', 'Ice Palace Exit', 'Misery Mire Exit',
'Palace of Darkness Exit', 'Swamp Palace Exit', 'Ganons Tower Exit', 'Desert Palace Exit (North)', 'Agahnims Tower Exit', 'Spiral Cave Exit (Top)',
'Superbunny Cave Exit (Bottom)', 'Turtle Rock Ledge Exit (East)']:
# For exits that connot be reached from another, no need to apply offset fixes.
rom.write_int16_to_rom(0x15DB5 + 2 * offset, link_y) # same as final else
elif room_id == 0x0059 and world.fix_skullwoods_exit:
rom.write_int16_to_rom(0x15DB5 + 2 * offset, 0x00F8)
elif room_id == 0x004a and world.fix_palaceofdarkness_exit:
rom.write_int16_to_rom(0x15DB5 + 2 * offset, 0x0640)
elif room_id == 0x00d6 and world.fix_trock_exit:
rom.write_int16_to_rom(0x15DB5 + 2 * offset, 0x0134)
elif room_id == 0x000c and world.fix_gtower_exit: # fix ganons tower exit point
rom.write_int16_to_rom(0x15DB5 + 2 * offset, 0x00A4)
else:
rom.write_int16_to_rom(0x15DB5 + 2 * offset, link_y)
rom.write_int16_to_rom(0x15E53 + 2 * offset, link_x)
rom.write_int16_to_rom(0x15EF1 + 2 * offset, camera_y)
rom.write_int16_to_rom(0x15F8F + 2 * offset, camera_x)
rom.write_byte(0x1602D + offset, unknown_1)
rom.write_byte(0x1607C + offset, unknown_2)
rom.write_int16_to_rom(0x160CB + 2 * offset, door_1)
rom.write_int16_to_rom(0x16169 + 2 * offset, door_2)
elif isinstance(exit.addresses, list):
# is hole
for address in exit.addresses:
rom.write_byte(address, exit.target)
else:
# patch door table
rom.write_byte(0xDBB73 + exit.addresses, exit.target)
# patch medallion requirements # patch medallion requirements
if world.required_medallions[0] == 'Bombos': if world.required_medallions[0] == 'Bombos':
@ -354,6 +405,7 @@ def patch_rom(world, rom, hashtable, beep='normal', sprite=None):
TRIFORCE_PIECE = ItemFactory('Triforce Piece').code TRIFORCE_PIECE = ItemFactory('Triforce Piece').code
GREEN_CLOCK = ItemFactory('Green Clock').code GREEN_CLOCK = ItemFactory('Green Clock').code
rom.write_byte(0x18004F, 0x01) # Byrna Invulnerability: on
# handle difficulty # handle difficulty
if world.difficulty == 'hard': if world.difficulty == 'hard':
# Powdered Fairies Prize # Powdered Fairies Prize
@ -364,13 +416,13 @@ def patch_rom(world, rom, hashtable, beep='normal', sprite=None):
rom.write_byte(0x180085, 0x40) # Half Magic rom.write_byte(0x180085, 0x40) # Half Magic
#Cape magic cost #Cape magic cost
rom.write_bytes(0x3ADA7, [0x02, 0x02, 0x02]) rom.write_bytes(0x3ADA7, [0x02, 0x02, 0x02])
#Byrna residual magic cost # Byrna Invulnerability: off
rom.write_bytes(0x45C42, [0x08, 0x08, 0x08]) rom.write_byte(0x18004F, 0x00)
#Disable catching fairies #Disable catching fairies
rom.write_byte(0x34FD6, 0x80) rom.write_byte(0x34FD6, 0x80)
overflow_replacement = GREEN_TWENTY_RUPEES overflow_replacement = GREEN_TWENTY_RUPEES
# Rupoor negative value # Rupoor negative value
rom.write_int16_to_rom(0x180036, 10) rom.write_int16_to_rom(0x180036, world.rupoor_cost)
#Make Blue Shield more expensive #Make Blue Shield more expensive
rom.write_bytes(0xF73D2, [0xFC, 0xFF]) rom.write_bytes(0xF73D2, [0xFC, 0xFF])
rom.write_bytes(0xF73DA, [0x04, 0x00]) rom.write_bytes(0xF73DA, [0x04, 0x00])
@ -398,8 +450,8 @@ def patch_rom(world, rom, hashtable, beep='normal', sprite=None):
rom.write_byte(0x180085, 0x20) # Quarter Magic rom.write_byte(0x180085, 0x20) # Quarter Magic
#Cape magic cost #Cape magic cost
rom.write_bytes(0x3ADA7, [0x02, 0x02, 0x02]) rom.write_bytes(0x3ADA7, [0x02, 0x02, 0x02])
#Byrna residual magic cost # Byrna Invulnerability: off
rom.write_bytes(0x45C42, [0x08, 0x08, 0x08]) rom.write_byte(0x18004F, 0x00)
#Disable catching fairies #Disable catching fairies
rom.write_byte(0x34FD6, 0x80) rom.write_byte(0x34FD6, 0x80)
overflow_replacement = GREEN_TWENTY_RUPEES overflow_replacement = GREEN_TWENTY_RUPEES
@ -432,8 +484,8 @@ def patch_rom(world, rom, hashtable, beep='normal', sprite=None):
rom.write_byte(0x180085, 0x00) # No healing rom.write_byte(0x180085, 0x00) # No healing
#Cape magic cost #Cape magic cost
rom.write_bytes(0x3ADA7, [0x02, 0x02, 0x02]) rom.write_bytes(0x3ADA7, [0x02, 0x02, 0x02])
#Byrna residual magic cost # Byrna Invulnerability: off
rom.write_bytes(0x45C42, [0x08, 0x08, 0x08]) rom.write_byte(0x18004F, 0x00)
#Disable catching fairies #Disable catching fairies
rom.write_byte(0x34FD6, 0x80) rom.write_byte(0x34FD6, 0x80)
overflow_replacement = GREEN_TWENTY_RUPEES overflow_replacement = GREEN_TWENTY_RUPEES
@ -466,8 +518,8 @@ def patch_rom(world, rom, hashtable, beep='normal', sprite=None):
rom.write_byte(0x180085, 0x80) # full rom.write_byte(0x180085, 0x80) # full
#Cape magic cost #Cape magic cost
rom.write_bytes(0x3ADA7, [0x04, 0x08, 0x10]) rom.write_bytes(0x3ADA7, [0x04, 0x08, 0x10])
#Byrna residual magic cost # Byrna Invulnerability: on
rom.write_bytes(0x45C42, [0x04, 0x02, 0x01]) rom.write_byte(0x18004F, 0x01)
#Enable catching fairies #Enable catching fairies
rom.write_byte(0x34FD6, 0xF0) rom.write_byte(0x34FD6, 0xF0)
#Set overflow items for progressive equipment #Set overflow items for progressive equipment
@ -478,6 +530,9 @@ def patch_rom(world, rom, hashtable, beep='normal', sprite=None):
else: else:
overflow_replacement = GREEN_TWENTY_RUPEES overflow_replacement = GREEN_TWENTY_RUPEES
#Byrna residual magic cost
rom.write_bytes(0x45C42, [0x04, 0x02, 0x01])
difficulty = world.difficulty_requirements difficulty = world.difficulty_requirements
#Set overflow items for progressive equipment #Set overflow items for progressive equipment
rom.write_bytes(0x180090, rom.write_bytes(0x180090,
@ -548,6 +603,20 @@ def patch_rom(world, rom, hashtable, beep='normal', sprite=None):
for prize, address in zip(bonk_prizes, bonk_addresses): for prize, address in zip(bonk_prizes, bonk_addresses):
rom.write_byte(address, prize) rom.write_byte(address, prize)
# Fill in item substitutions table
if world.difficulty in ['easy']:
rom.write_bytes(0x184000, [
# original_item, limit, replacement_item, filler
0x12, 0x01, 0x35, 0xFF, # lamp -> 5 rupees
0x58, 0x01, 0x43, 0xFF, # silver arrows -> 1 arrow
0xFF, 0xFF, 0xFF, 0xFF, # end of table sentinel
])
else:
rom.write_bytes(0x184000, [
# original_item, limit, replacement_item, filler
0x12, 0x01, 0x35, 0xFF, # lamp -> 5 rupees
0xFF, 0xFF, 0xFF, 0xFF, # end of table sentinel
])
# set Fountain bottle exchange items # set Fountain bottle exchange items
if world.difficulty in ['hard', 'expert', 'insane']: if world.difficulty in ['hard', 'expert', 'insane']:
@ -637,11 +706,14 @@ def patch_rom(world, rom, hashtable, beep='normal', sprite=None):
# TODO: a proper race rom mode should be implemented, that changes the following flag, and rummages the table (or uses the future encryption feature, etc) # TODO: a proper race rom mode should be implemented, that changes the following flag, and rummages the table (or uses the future encryption feature, etc)
rom.write_bytes(0x180213, [0x00, 0x01]) # Not a Tournament Seed rom.write_bytes(0x180213, [0x00, 0x01]) # Not a Tournament Seed
rom.write_byte(0x180211, 0x06) #Game type, we set the Entrance and item randomization flags
# assorted fixes # assorted fixes
rom.write_byte(0x180030, 0x00) # Disable SRAM trace rom.write_byte(0x180030, 0x00) # Disable SRAM trace
rom.write_byte(0x1800A2, 0x01) # remain in real dark world when dying in dark word dungion before killing aga1 rom.write_byte(0x1800A2, 0x01) # remain in real dark world when dying in dark word dungion before killing aga1
rom.write_byte(0x180169, 0x01 if world.lock_aga_door_in_escape else 0x00) # Lock or unlock aga tower door during escape sequence. rom.write_byte(0x180169, 0x01 if world.lock_aga_door_in_escape else 0x00) # Lock or unlock aga tower door during escape sequence.
rom.write_byte(0x180171, 0x01 if world.ganon_at_pyramid else 0x00) # Enable respawning on pyramid after ganon death rom.write_byte(0x180171, 0x01 if world.ganon_at_pyramid else 0x00) # Enable respawning on pyramid after ganon death
rom.write_byte(0x180173, 0x01) # Bob is enabled
rom.write_byte(0x180168, 0x08) # Spike Cave Damage rom.write_byte(0x180168, 0x08) # Spike Cave Damage
rom.write_bytes(0x18016B, [0x04, 0x02, 0x01]) #Set spike cave and MM spike room Cape usage rom.write_bytes(0x18016B, [0x04, 0x02, 0x01]) #Set spike cave and MM spike room Cape usage
rom.write_bytes(0x18016E, [0x04, 0x08, 0x10]) #Set spike cave and MM spike room Cape usage rom.write_bytes(0x18016E, [0x04, 0x08, 0x10]) #Set spike cave and MM spike room Cape usage
@ -653,8 +725,26 @@ def patch_rom(world, rom, hashtable, beep='normal', sprite=None):
rom.write_byte(0x180086, 0x00 if world.aga_randomness else 0x01) # set blue ball and ganon warp randomness rom.write_byte(0x180086, 0x00 if world.aga_randomness else 0x01) # set blue ball and ganon warp randomness
rom.write_byte(0x1800A0, 0x01) # return to light world on s+q without mirror rom.write_byte(0x1800A0, 0x01) # return to light world on s+q without mirror
rom.write_byte(0x1800A1, 0x01) # enable overworld screen transition draining for water level inside swamp rom.write_byte(0x1800A1, 0x01) # enable overworld screen transition draining for water level inside swamp
rom.write_byte(0x180174, 0x01 if world.fix_fake_world else 0x00)
rom.write_byte(0x180175, 0x00) # Arrow mode: normal
rom.write_int16_to_rom(0x180176, 0) # Wood Arrow Cost (rupee arrow mode)
rom.write_int16_to_rom(0x180178, 0) # Silver Arrow Cost (rupee arrow mode)
rom.write_byte(0x180034, 0x0A) # starting max bombs rom.write_byte(0x180034, 0x0A) # starting max bombs
rom.write_byte(0x180035, 30) # starting max bombs rom.write_byte(0x180035, 30) # starting max arrows
for x in range(0x183000, 0x18304F):
rom.write_byte(x, 0) # Zero the initial equipment array
rom.write_byte(0x18302C, 0x18) # starting max health
rom.write_byte(0x18302D, 0x18) # starting current health
rom.write_byte(0x183039, 0x68) # starting abilities, bit array
rom.write_byte(0x18004A, 0x00) # Inverted mode (off)
rom.write_byte(0x2AF79, 0xD0) # vortexes: Normal (D0=light to dark, F0=dark to light, 42 = both)
rom.write_byte(0x3A943, 0xD0) # Mirror: Normal (D0=Dark to Light, F0=light to dark, 42 = both)
rom.write_byte(0x3A96D, 0xF0) # Residual Portal: Normal (F0= Light Side, D0=Dark Side, 42 = both (Darth Vader))
rom.write_byte(0x3A9A7, 0xD0) # Residual Portal: Normal (D0= Light Side, F0=Dark Side, 42 = both (Darth Vader))
rom.write_byte(0x18004D, 0x00) # Escape assist (off)
rom.write_byte(0x18004E, 0x00) # uncle Refill (off)
if world.goal in ['pedestal', 'triforcehunt']: if world.goal in ['pedestal', 'triforcehunt']:
rom.write_byte(0x18003E, 0x01) # make ganon invincible rom.write_byte(0x18003E, 0x01) # make ganon invincible
@ -694,10 +784,20 @@ def patch_rom(world, rom, hashtable, beep='normal', sprite=None):
rom.write_bytes(0x6D2FB, [0x00, 0x00, 0xf7, 0xff, 0x02, 0x0E]) rom.write_bytes(0x6D2FB, [0x00, 0x00, 0xf7, 0xff, 0x02, 0x0E])
rom.write_bytes(0x6D313, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E]) rom.write_bytes(0x6D313, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E])
# Shop table
rom.write_bytes(0x184800, [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])
# patch swamp: Need to enable permanent drain of water as dam or swamp were moved # patch swamp: Need to enable permanent drain of water as dam or swamp were moved
rom.write_byte(0x18003D, 0x01 if world.swamp_patch_required else 0x00) rom.write_byte(0x18003D, 0x01 if world.swamp_patch_required else 0x00)
# powder patch: remove the need to leave the scrren after powder, since it causes problems for potion shop at race game
# temporarally we are just nopping out this check we will conver this to a rom fix soon.
rom.write_bytes(0x02F539,[0xEA,0xEA,0xEA,0xEA,0xEA] if world.powder_patch_required else [0xAD, 0xBF, 0x0A, 0xF0, 0x4F])
# allow smith into multi-entrance caves in appropriate shuffles
if world.shuffle in ['restricted', 'full', 'crossed', 'insanity']:
rom.write_byte(0x18004C, 0x01)
# set correct flag for hera basement item # set correct flag for hera basement item
if world.get_location('Tower of Hera - Basement Cage').item is not None and world.get_location('Tower of Hera - Basement Cage').item.name == 'Small Key (Tower of Hera)': if world.get_location('Tower of Hera - Basement Cage').item is not None and world.get_location('Tower of Hera - Basement Cage').item.name == 'Small Key (Tower of Hera)':
rom.write_byte(0x4E3BB, 0xE4) rom.write_byte(0x4E3BB, 0xE4)
@ -714,34 +814,20 @@ def patch_rom(world, rom, hashtable, beep='normal', sprite=None):
rom.write_byte(0xFED31, 0x2A) # preopen bombable exit rom.write_byte(0xFED31, 0x2A) # preopen bombable exit
rom.write_byte(0xFEE41, 0x2A) # preopen bombable exit rom.write_byte(0xFEE41, 0x2A) # preopen bombable exit
# Thanks to Zarby89 for finding these values
# fix skull woods exit point
rom.write_byte(0x15E0D, 0xF8 if world.fix_skullwoods_exit else 0xB8)
# fix palace of darkness exit point
rom.write_byte(0x15E03, 0x40 if world.fix_palaceofdarkness_exit else 0x28)
# fix turtle rock exit point
rom.write_byte(0x15E1D, 0x34 if world.fix_trock_exit else 0x28)
# fix ganons tower exit point
rom.write_byte(0x15E25, 0xA4 if world.fix_gtower_exit else 0x28)
# todo fix screen scrolling
write_strings(rom, world) write_strings(rom, world)
# set rom name # set rom name
# 21 bytes # 21 bytes
rom.write_bytes(0x7FC0, bytearray('ER_052_%09d\0' % world.seed, 'utf8') + world.option_identifier.to_bytes(4, 'big')) rom.write_bytes(0x7FC0, bytearray('ER_060_%09d\0' % world.seed, 'utf8') + world.option_identifier.to_bytes(4, 'big'))
# store hash table for main menu hash # store hash table for main menu hash
rom.write_bytes(0x187F00, hashtable) rom.write_bytes(0x187F00, hashtable)
apply_rom_settings(rom, beep, world.quickswap, world.fastmenu, world.disable_music, sprite) apply_rom_settings(rom, beep, color, world.quickswap, world.fastmenu, world.disable_music, sprite)
return rom return rom
def apply_rom_settings(rom, beep, quickswap, fastmenu, disable_music, sprite): def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, sprite):
# enable instant item menu # enable instant item menu
if fastmenu == 'instant': if fastmenu == 'instant':
@ -765,99 +851,92 @@ def apply_rom_settings(rom, beep, quickswap, fastmenu, disable_music, sprite):
else: else:
rom.write_byte(0x180048, 0x08) rom.write_byte(0x180048, 0x08)
# enable quick item swapping with L and R (ported by Amazing Ampharos) rom.write_byte(0x18004B, 0x01 if quickswap else 0x00)
if quickswap:
rom.write_bytes(0x107fb, [0x22, 0x50, 0xFF, 0x1F])
rom.write_bytes(0x12451, [0x22, 0x50, 0xFF, 0x1F])
rom.write_bytes(0xfff50, [0x20, 0x58, 0xFF, 0xA5, 0xF6, 0x29, 0x40, 0x6B, 0xA5, 0xF6, 0x89, 0x10, 0xF0, 0x03, 0x4C, 0x69,
0xFF, 0x89, 0x20, 0xF0, 0x03, 0x4C, 0xAA, 0xFF, 0x60, 0xAD, 0x02, 0x02, 0xF0, 0x3B, 0xDA, 0xAA,
0xE0, 0x0F, 0xF0, 0x14, 0xE0, 0x10, 0xF0, 0x14, 0xE0, 0x14, 0xD0, 0x02, 0xA2, 0x00, 0xE8, 0xBF,
0x3F, 0xF3, 0x7E, 0xF0, 0xEB, 0x4C, 0xEB, 0xFF, 0xA2, 0x01, 0x80, 0x0A, 0xAF, 0x4F, 0xF3, 0x7E,
0xAA, 0xE0, 0x04, 0xF0, 0x10, 0xE8, 0xBF, 0x5B, 0xF3, 0x7E, 0xF0, 0xF5, 0x8A, 0x8F, 0x4F, 0xF3,
0x7E, 0xA2, 0x10, 0x80, 0xE0, 0xA2, 0x11, 0x80, 0xD6, 0x60, 0xAD, 0x02, 0x02, 0xF0, 0x3B, 0xDA,
0xAA, 0xE0, 0x11, 0xF0, 0x14, 0xE0, 0x10, 0xF0, 0x14, 0xE0, 0x01, 0xD0, 0x02, 0xA2, 0x15, 0xCA,
0xBF, 0x3F, 0xF3, 0x7E, 0xF0, 0xEB, 0x4C, 0xEB, 0xFF, 0xA2, 0x04, 0x80, 0x0A, 0xAF, 0x4F, 0xF3,
0x7E, 0xAA, 0xE0, 0x01, 0xF0, 0x10, 0xCA, 0xBF, 0x5B, 0xF3, 0x7E, 0xF0, 0xF5, 0x8A, 0x8F, 0x4F,
0xF3, 0x7E, 0xA2, 0x10, 0x80, 0xE0, 0xA2, 0x0F, 0x80, 0xD6, 0x60, 0xA9, 0x20, 0x8D, 0x2F, 0x01,
0x8E, 0x02, 0x02, 0x22, 0x7F, 0xDB, 0x0D, 0xFA, 0x60, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])
else:
rom.write_bytes(0x107fb, [0xa5, 0xf6, 0x29, 0x40])
rom.write_bytes(0x12451, [0xa5, 0xf6, 0x29, 0x40])
rom.write_bytes(0xfff50, [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])
music_volumes = [ music_volumes = [
(0x00, [0xD373B, 0xD375B, 0xD90F8]), (0x00, [0xD373B, 0xD375B, 0xD90F8]),
(0x14, [0xDA710, 0xDA7A4, 0xDA7BB, 0xDA7D2]), (0x14, [0xDA710, 0xDA7A4, 0xDA7BB, 0xDA7D2]),
(0x3C, [0xD5954, 0xD653B, 0xDA736, 0xDA752, 0xDA772, 0xDA792]), (0x3C, [0xD5954, 0xD653B, 0xDA736, 0xDA752, 0xDA772, 0xDA792]),
(0x50, [0xD5B47, 0xD5B5E]), (0x50, [0xD5B47, 0xD5B5E]),
(0x54, [0xD4306]), (0x54, [0xD4306]),
(0x64, [0xD6878, 0xD6883, 0xD6E48, 0xD6E76, 0xD6EFB, 0xD6F2D, 0xDA211, 0xDA35B, 0xDA37B, 0xDA38E, 0xDA39F, 0xDA5C3, 0xDA691, 0xDA6A8, 0xDA6DF]), (0x64, [0xD6878, 0xD6883, 0xD6E48, 0xD6E76, 0xD6EFB, 0xD6F2D, 0xDA211, 0xDA35B, 0xDA37B, 0xDA38E, 0xDA39F, 0xDA5C3, 0xDA691, 0xDA6A8, 0xDA6DF]),
(0x78, [0xD2349, 0xD3F45, 0xD42EB, 0xD48B9, 0xD48FF, 0xD543F, 0xD5817, 0xD5957, 0xD5ACB, 0xD5AE8, 0xD5B4A, 0xDA5DE, 0xDA608, 0xDA635, (0x78, [0xD2349, 0xD3F45, 0xD42EB, 0xD48B9, 0xD48FF, 0xD543F, 0xD5817, 0xD5957, 0xD5ACB, 0xD5AE8, 0xD5B4A, 0xDA5DE, 0xDA608, 0xDA635,
0xDA662, 0xDA71F, 0xDA7AF, 0xDA7C6, 0xDA7DD]), 0xDA662, 0xDA71F, 0xDA7AF, 0xDA7C6, 0xDA7DD]),
(0x82, [0xD2F00, 0xDA3D5]), (0x82, [0xD2F00, 0xDA3D5]),
(0xA0, [0xD249C, 0xD24CD, 0xD2C09, 0xD2C53, 0xD2CAF, 0xD2CEB, 0xD2D91, 0xD2EE6, 0xD38ED, 0xD3C91, 0xD3CD3, 0xD3CE8, 0xD3F0C, (0xA0, [0xD249C, 0xD24CD, 0xD2C09, 0xD2C53, 0xD2CAF, 0xD2CEB, 0xD2D91, 0xD2EE6, 0xD38ED, 0xD3C91, 0xD3CD3, 0xD3CE8, 0xD3F0C,
0xD3F82, 0xD405F, 0xD4139, 0xD4198, 0xD41D5, 0xD41F6, 0xD422B, 0xD4270, 0xD42B1, 0xD4334, 0xD4371, 0xD43A6, 0xD43DB, 0xD3F82, 0xD405F, 0xD4139, 0xD4198, 0xD41D5, 0xD41F6, 0xD422B, 0xD4270, 0xD42B1, 0xD4334, 0xD4371, 0xD43A6, 0xD43DB,
0xD441E, 0xD4597, 0xD4B3C, 0xD4BAB, 0xD4C03, 0xD4C53, 0xD4C7F, 0xD4D9C, 0xD5424, 0xD65D2, 0xD664F, 0xD6698, 0xD66FF, 0xD441E, 0xD4597, 0xD4B3C, 0xD4BAB, 0xD4C03, 0xD4C53, 0xD4C7F, 0xD4D9C, 0xD5424, 0xD65D2, 0xD664F, 0xD6698, 0xD66FF,
0xD6985, 0xD6C5C, 0xD6C6F, 0xD6C8E, 0xD6CB4, 0xD6D7D, 0xD827D, 0xD960C, 0xD9828, 0xDA233, 0xDA3A2, 0xDA49E, 0xDA72B, 0xD6985, 0xD6C5C, 0xD6C6F, 0xD6C8E, 0xD6CB4, 0xD6D7D, 0xD827D, 0xD960C, 0xD9828, 0xDA233, 0xDA3A2, 0xDA49E, 0xDA72B,
0xDA745, 0xDA765, 0xDA785, 0xDABF6, 0xDAC0D, 0xDAEBE, 0xDAFAC]), 0xDA745, 0xDA765, 0xDA785, 0xDABF6, 0xDAC0D, 0xDAEBE, 0xDAFAC]),
(0xAA, [0xD9A02, 0xD9BD6]), (0xAA, [0xD9A02, 0xD9BD6]),
(0xB4, [0xD21CD, 0xD2279, 0xD2E66, 0xD2E70, 0xD2EAB, 0xD3B97, 0xD3BAC, 0xD3BE8, 0xD3C0D, 0xD3C39, 0xD3C68, 0xD3C9F, 0xD3CBC, (0xB4, [0xD21CD, 0xD2279, 0xD2E66, 0xD2E70, 0xD2EAB, 0xD3B97, 0xD3BAC, 0xD3BE8, 0xD3C0D, 0xD3C39, 0xD3C68, 0xD3C9F, 0xD3CBC,
0xD401E, 0xD4290, 0xD443E, 0xD456F, 0xD47D3, 0xD4D43, 0xD4DCC, 0xD4EBA, 0xD4F0B, 0xD4FE5, 0xD5012, 0xD54BC, 0xD54D5, 0xD401E, 0xD4290, 0xD443E, 0xD456F, 0xD47D3, 0xD4D43, 0xD4DCC, 0xD4EBA, 0xD4F0B, 0xD4FE5, 0xD5012, 0xD54BC, 0xD54D5,
0xD54F0, 0xD5509, 0xD57D8, 0xD59B9, 0xD5A2F, 0xD5AEB, 0xD5E5E, 0xD5FE9, 0xD658F, 0xD674A, 0xD6827, 0xD69D6, 0xD69F5, 0xD54F0, 0xD5509, 0xD57D8, 0xD59B9, 0xD5A2F, 0xD5AEB, 0xD5E5E, 0xD5FE9, 0xD658F, 0xD674A, 0xD6827, 0xD69D6, 0xD69F5,
0xD6A05, 0xD6AE9, 0xD6DCF, 0xD6E20, 0xD6ECB, 0xD71D4, 0xD71E6, 0xD7203, 0xD721E, 0xD8724, 0xD8732, 0xD9652, 0xD9698, 0xD6A05, 0xD6AE9, 0xD6DCF, 0xD6E20, 0xD6ECB, 0xD71D4, 0xD71E6, 0xD7203, 0xD721E, 0xD8724, 0xD8732, 0xD9652, 0xD9698,
0xD9CBC, 0xD9DC0, 0xD9E49, 0xDAA68, 0xDAA77, 0xDAA88, 0xDAA99, 0xDAF04]), 0xD9CBC, 0xD9DC0, 0xD9E49, 0xDAA68, 0xDAA77, 0xDAA88, 0xDAA99, 0xDAF04]),
(0x8c, [0xD1D28, 0xD1D41, 0xD1D5C, 0xD1D77, 0xD1EEE, 0xD311D, 0xD31D1, 0xD4148, 0xD5543, 0xD5B6F, 0xD65B3, 0xD6760, 0xD6B6B, (0x8c, [0xD1D28, 0xD1D41, 0xD1D5C, 0xD1D77, 0xD1EEE, 0xD311D, 0xD31D1, 0xD4148, 0xD5543, 0xD5B6F, 0xD65B3, 0xD6760, 0xD6B6B,
0xD6DF6, 0xD6E0D, 0xD73A1, 0xD814C, 0xD825D, 0xD82BE, 0xD8340, 0xD8394, 0xD842C, 0xD8796, 0xD8903, 0xD892A, 0xD91E8, 0xD6DF6, 0xD6E0D, 0xD73A1, 0xD814C, 0xD825D, 0xD82BE, 0xD8340, 0xD8394, 0xD842C, 0xD8796, 0xD8903, 0xD892A, 0xD91E8,
0xD922B, 0xD92E0, 0xD937E, 0xD93C1, 0xDA958, 0xDA971, 0xDA98C, 0xDA9A7]), 0xD922B, 0xD92E0, 0xD937E, 0xD93C1, 0xDA958, 0xDA971, 0xDA98C, 0xDA9A7]),
(0xC8, [0xD1D92, 0xD1DBD, 0xD1DEB, 0xD1F5D, 0xD1F9F, 0xD1FBD, 0xD1FDC, 0xD1FEA, 0xD20CA, 0xD21BB, 0xD22C9, 0xD2754, 0xD284C, (0xC8, [0xD1D92, 0xD1DBD, 0xD1DEB, 0xD1F5D, 0xD1F9F, 0xD1FBD, 0xD1FDC, 0xD1FEA, 0xD20CA, 0xD21BB, 0xD22C9, 0xD2754, 0xD284C,
0xD2866, 0xD2887, 0xD28A0, 0xD28BA, 0xD28DB, 0xD28F4, 0xD293E, 0xD2BF3, 0xD2C1F, 0xD2C69, 0xD2CA1, 0xD2CC5, 0xD2D05, 0xD2866, 0xD2887, 0xD28A0, 0xD28BA, 0xD28DB, 0xD28F4, 0xD293E, 0xD2BF3, 0xD2C1F, 0xD2C69, 0xD2CA1, 0xD2CC5, 0xD2D05,
0xD2D73, 0xD2DAF, 0xD2E3D, 0xD2F36, 0xD2F46, 0xD2F6F, 0xD2FCF, 0xD2FDF, 0xD302B, 0xD3086, 0xD3099, 0xD30A5, 0xD30CD, 0xD2D73, 0xD2DAF, 0xD2E3D, 0xD2F36, 0xD2F46, 0xD2F6F, 0xD2FCF, 0xD2FDF, 0xD302B, 0xD3086, 0xD3099, 0xD30A5, 0xD30CD,
0xD30F6, 0xD3154, 0xD3184, 0xD333A, 0xD33D9, 0xD349F, 0xD354A, 0xD35E5, 0xD3624, 0xD363C, 0xD3672, 0xD3691, 0xD36B4, 0xD30F6, 0xD3154, 0xD3184, 0xD333A, 0xD33D9, 0xD349F, 0xD354A, 0xD35E5, 0xD3624, 0xD363C, 0xD3672, 0xD3691, 0xD36B4,
0xD36C6, 0xD3724, 0xD3767, 0xD38CB, 0xD3B1D, 0xD3B2F, 0xD3B55, 0xD3B70, 0xD3B81, 0xD3BBF, 0xD3D34, 0xD3D55, 0xD3D6E, 0xD36C6, 0xD3724, 0xD3767, 0xD38CB, 0xD3B1D, 0xD3B2F, 0xD3B55, 0xD3B70, 0xD3B81, 0xD3BBF, 0xD3F65, 0xD3FA6, 0xD404F,
0xD3DC6, 0xD3E04, 0xD3E38, 0xD3F65, 0xD3FA6, 0xD404F, 0xD4087, 0xD417A, 0xD41A0, 0xD425C, 0xD4319, 0xD433C, 0xD43EF, 0xD4087, 0xD417A, 0xD41A0, 0xD425C, 0xD4319, 0xD433C, 0xD43EF, 0xD440C, 0xD4452, 0xD4494, 0xD44B5, 0xD4512, 0xD45D1,
0xD440C, 0xD4452, 0xD4494, 0xD44B5, 0xD4512, 0xD45D1, 0xD45EF, 0xD4682, 0xD46C3, 0xD483C, 0xD4848, 0xD4855, 0xD4862, 0xD45EF, 0xD4682, 0xD46C3, 0xD483C, 0xD4848, 0xD4855, 0xD4862, 0xD486F, 0xD487C, 0xD4A1C, 0xD4A3B, 0xD4A60, 0xD4B27,
0xD486F, 0xD487C, 0xD4A1C, 0xD4A3B, 0xD4A60, 0xD4B27, 0xD4C7A, 0xD4D12, 0xD4D81, 0xD4E90, 0xD4ED6, 0xD4EE2, 0xD5005, 0xD4C7A, 0xD4D12, 0xD4D81, 0xD4E90, 0xD4ED6, 0xD4EE2, 0xD5005, 0xD502E, 0xD503C, 0xD5081, 0xD51B1, 0xD51C7, 0xD51CF,
0xD502E, 0xD503C, 0xD5081, 0xD51B1, 0xD51C7, 0xD51CF, 0xD51EF, 0xD520C, 0xD5214, 0xD5231, 0xD5257, 0xD526D, 0xD5275, 0xD51EF, 0xD520C, 0xD5214, 0xD5231, 0xD5257, 0xD526D, 0xD5275, 0xD52AF, 0xD52BD, 0xD52CD, 0xD52DB, 0xD549C, 0xD5801,
0xD52AF, 0xD52BD, 0xD52CD, 0xD52DB, 0xD549C, 0xD5801, 0xD58A4, 0xD5A68, 0xD5A7F, 0xD5C12, 0xD5D71, 0xD5E10, 0xD5E9A, 0xD58A4, 0xD5A68, 0xD5A7F, 0xD5C12, 0xD5D71, 0xD5E10, 0xD5E9A, 0xD5F8B, 0xD5FA4, 0xD651A, 0xD6542, 0xD65ED, 0xD661D,
0xD5F8B, 0xD5FA4, 0xD651A, 0xD6542, 0xD65ED, 0xD661D, 0xD66D7, 0xD6776, 0xD68BD, 0xD68E5, 0xD6956, 0xD6973, 0xD69A8, 0xD66D7, 0xD6776, 0xD68BD, 0xD68E5, 0xD6956, 0xD6973, 0xD69A8, 0xD6A51, 0xD6A86, 0xD6B96, 0xD6C3E, 0xD6D4A, 0xD6E9C,
0xD6A51, 0xD6A86, 0xD6B96, 0xD6C3E, 0xD6D4A, 0xD6E9C, 0xD6F80, 0xD717E, 0xD7190, 0xD71B9, 0xD811D, 0xD8139, 0xD816B, 0xD6F80, 0xD717E, 0xD7190, 0xD71B9, 0xD811D, 0xD8139, 0xD816B, 0xD818A, 0xD819E, 0xD81BE, 0xD829C, 0xD82E1, 0xD8306,
0xD818A, 0xD819E, 0xD81BE, 0xD829C, 0xD82E1, 0xD8306, 0xD830E, 0xD835E, 0xD83AB, 0xD83CA, 0xD83F0, 0xD83F8, 0xD844B, 0xD830E, 0xD835E, 0xD83AB, 0xD83CA, 0xD83F0, 0xD83F8, 0xD844B, 0xD8479, 0xD849E, 0xD84CB, 0xD84EB, 0xD84F3, 0xD854A,
0xD8479, 0xD849E, 0xD84CB, 0xD84EB, 0xD84F3, 0xD854A, 0xD8573, 0xD859D, 0xD85B4, 0xD85CE, 0xD862A, 0xD8681, 0xD87E3, 0xD8573, 0xD859D, 0xD85B4, 0xD85CE, 0xD862A, 0xD8681, 0xD87E3, 0xD87FF, 0xD887B, 0xD88C6, 0xD88E3, 0xD8944, 0xD897B,
0xD87FF, 0xD887B, 0xD88C6, 0xD88E3, 0xD8944, 0xD897B, 0xD8C97, 0xD8CA4, 0xD8CB3, 0xD8CC2, 0xD8CD1, 0xD8D01, 0xD917B, 0xD8C97, 0xD8CA4, 0xD8CB3, 0xD8CC2, 0xD8CD1, 0xD8D01, 0xD917B, 0xD918C, 0xD919A, 0xD91B5, 0xD91D0, 0xD91DD, 0xD9220,
0xD918C, 0xD919A, 0xD91B5, 0xD91D0, 0xD91DD, 0xD9220, 0xD9273, 0xD9284, 0xD9292, 0xD92AD, 0xD92C8, 0xD92D5, 0xD9311, 0xD9273, 0xD9284, 0xD9292, 0xD92AD, 0xD92C8, 0xD92D5, 0xD9311, 0xD9322, 0xD9330, 0xD934B, 0xD9366, 0xD9373, 0xD93B6,
0xD9322, 0xD9330, 0xD934B, 0xD9366, 0xD9373, 0xD93B6, 0xD97A6, 0xD97C2, 0xD97DC, 0xD97FB, 0xD9811, 0xD98FF, 0xD996F, 0xD97A6, 0xD97C2, 0xD97DC, 0xD97FB, 0xD9811, 0xD98FF, 0xD996F, 0xD99A8, 0xD99D5, 0xD9A30, 0xD9A4E, 0xD9A6B, 0xD9A88,
0xD99A8, 0xD99D5, 0xD9A30, 0xD9A4E, 0xD9A6B, 0xD9A88, 0xD9AF7, 0xD9B1D, 0xD9B43, 0xD9B7C, 0xD9BA9, 0xD9C84, 0xD9C8D, 0xD9AF7, 0xD9B1D, 0xD9B43, 0xD9B7C, 0xD9BA9, 0xD9C84, 0xD9C8D, 0xD9CAC, 0xD9CE8, 0xD9CF3, 0xD9CFD, 0xD9D46, 0xDA35E,
0xD9CAC, 0xD9CE8, 0xD9CF3, 0xD9CFD, 0xD9D46, 0xDA35E, 0xDA37E, 0xDA391, 0xDA478, 0xDA4C3, 0xDA4D7, 0xDA4F6, 0xDA515, 0xDA37E, 0xDA391, 0xDA478, 0xDA4C3, 0xDA4D7, 0xDA4F6, 0xDA515, 0xDA6E2, 0xDA9C2, 0xDA9ED, 0xDAA1B, 0xDAA57, 0xDABAF,
0xDA6E2, 0xDA9C2, 0xDA9ED, 0xDAA1B, 0xDAA57, 0xDABAF, 0xDABC9, 0xDABE2, 0xDAC28, 0xDAC46, 0xDAC63, 0xDACB8, 0xDACEC, 0xDABC9, 0xDABE2, 0xDAC28, 0xDAC46, 0xDAC63, 0xDACB8, 0xDACEC, 0xDAD08, 0xDAD25, 0xDAD42, 0xDAD5F, 0xDAE17, 0xDAE34,
0xDAD08, 0xDAD25, 0xDAD42, 0xDAD5F, 0xDAE17, 0xDAE34, 0xDAE51, 0xDAF2E, 0xDAF55, 0xDAF6B, 0xDAF81, 0xDB14F, 0xDB16B, 0xDAE51, 0xDAF2E, 0xDAF55, 0xDAF6B, 0xDAF81, 0xDB14F, 0xDB16B, 0xDB180, 0xDB195, 0xDB1AA]),
0xDB180, 0xDB195, 0xDB1AA]), (0xD2, [0xD2B88, 0xD364A, 0xD369F, 0xD3747]),
(0xD2, [0xD2B88, 0xD364A, 0xD369F, 0xD3747]), (0xDC, [0xD213F, 0xD2174, 0xD229E, 0xD2426, 0xD4731, 0xD4753, 0xD4774, 0xD4795, 0xD47B6, 0xD4AA5, 0xD4AE4, 0xD4B96, 0xD4CA5,
(0xDC, [0xD213F, 0xD2174, 0xD229E, 0xD2426, 0xD4731, 0xD4753, 0xD4774, 0xD4795, 0xD47B6, 0xD4AA5, 0xD4AE4, 0xD4B96, 0xD4CA5, 0xD5477, 0xD5A3D, 0xD6566, 0xD672C, 0xD67C0, 0xD69B8, 0xD6AB1, 0xD6C05, 0xD6DB3, 0xD71AB, 0xD8E2D, 0xD8F0D, 0xD94E0,
0xD5477, 0xD5A3D, 0xD6566, 0xD672C, 0xD67C0, 0xD69B8, 0xD6AB1, 0xD6C05, 0xD6DB3, 0xD71AB, 0xD8E2D, 0xD8F0D, 0xD94E0, 0xD9544, 0xD95A8, 0xD9982, 0xD9B56, 0xDA694, 0xDA6AB, 0xDAE88, 0xDAEC8, 0xDAEE6, 0xDB1BF]),
0xD9544, 0xD95A8, 0xD9982, 0xD9B56, 0xDA694, 0xDA6AB, 0xDAE88, 0xDAEC8, 0xDAEE6, 0xDB1BF]), (0xE6, [0xD210A, 0xD22DC, 0xD2447, 0xD5A4D, 0xD5DDC, 0xDA251, 0xDA26C]),
(0xE6, [0xD210A, 0xD22DC, 0xD2447, 0xD5A4D, 0xD5DDC, 0xDA251, 0xDA26C]), (0xF0, [0xD945E, 0xD967D, 0xD96C2, 0xD9C95, 0xD9EE6, 0xDA5C6]),
(0xF0, [0xD945E, 0xD967D, 0xD96C2, 0xD9C95, 0xD9EE6, 0xDA5C6]), (0xFA, [0xD2047, 0xD24C2, 0xD24EC, 0xD25A4, 0xD51A8, 0xD51E6, 0xD524E, 0xD529E, 0xD6045, 0xD81DE, 0xD821E, 0xD94AA, 0xD9A9E,
(0xFA, [0xD2047, 0xD24C2, 0xD24EC, 0xD25A4, 0xD3DAA, 0xD51A8, 0xD51E6, 0xD524E, 0xD529E, 0xD6045, 0xD81DE, 0xD821E, 0xD94AA, 0xD9AE4, 0xDA289]),
0xD9A9E, 0xD9AE4, 0xDA289]), (0xFF, [0xD2085, 0xD21C5, 0xD5F28])
(0xFF, [0xD2085, 0xD21C5, 0xD5F28])
] ]
for volume, addresses in music_volumes: for volume, addresses in music_volumes:
for address in addresses: for address in addresses:
rom.write_byte(address, volume if not disable_music else 0x00) rom.write_byte(address, volume if not disable_music else 0x00)
# restore Mirror sound effect volumes (for existing seeds that lack it)
rom.write_byte(0xD3E04, 0xC8)
rom.write_byte(0xD3DC6, 0xC8)
rom.write_byte(0xD3D6E, 0xC8)
rom.write_byte(0xD3D34, 0xC8)
rom.write_byte(0xD3D55, 0xC8)
rom.write_byte(0xD3E38, 0xC8)
rom.write_byte(0xD3DAA, 0xFA)
# set heart beep rate # set heart beep rate
rom.write_byte(0x180033, {'off': 0x00, 'half': 0x40, 'quarter': 0x80, 'normal': 0x20}[beep]) rom.write_byte(0x180033, {'off': 0x00, 'half': 0x40, 'quarter': 0x80, 'normal': 0x20}[beep])
# set heart color
rom.write_byte(0x6FA1E, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x6FA20, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x6FA22, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x6FA24, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x6FA26, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x6FA28, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x6FA2A, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x6FA2C, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x6FA2E, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x6FA30, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x65561, {'red': 0x05, 'blue': 0x0D, 'green': 0x19, 'yellow': 0x09}[color])
# write link sprite if required # write link sprite if required
if sprite is not None: if sprite is not None:
write_sprite(rom, sprite) write_sprite(rom, sprite)

316
Rules.py
View File

@ -1,3 +1,4 @@
import collections
import logging import logging
@ -52,7 +53,7 @@ def add_rule(spot, rule, combine='and'):
def add_lamp_requirement(spot): def add_lamp_requirement(spot):
add_rule(spot, lambda state: state.has('Lamp')) add_rule(spot, lambda state: state.has('Lamp', state.world.lamps_needed_for_dark_rooms))
def forbid_item(location, item): def forbid_item(location, item):
@ -86,7 +87,11 @@ def global_rules(world):
world.get_region('Old Man House').can_reach = lambda state: state.can_reach('Old Man', 'Location') or old_rule(state) world.get_region('Old Man House').can_reach = lambda state: state.can_reach('Old Man', 'Location') or old_rule(state)
# overworld requirements # overworld requirements
set_rule(world.get_entrance('Kings Grave'), lambda state: state.has_Boots() and (state.can_lift_heavy_rocks() or (state.has_Pearl() and state.has_Mirror() and state.can_reach('West Dark World')))) set_rule(world.get_entrance('Kings Grave'), lambda state: state.has_Boots())
set_rule(world.get_entrance('Kings Grave Outer Rocks'), lambda state: state.can_lift_heavy_rocks())
set_rule(world.get_entrance('Kings Grave Inner Rocks'), lambda state: state.can_lift_heavy_rocks())
set_rule(world.get_entrance('Kings Grave Mirror Spot'), lambda state: state.has_Pearl() and state.has_Mirror())
# Caution: If king's grave is releaxed at all to account for reaching it via a two way cave's exit in insanity mode, then the bomb shop logic will need to be updated (that would involve create a small ledge-like Region for it)
set_rule(world.get_entrance('Bonk Fairy (Light)'), lambda state: state.has_Boots()) set_rule(world.get_entrance('Bonk Fairy (Light)'), lambda state: state.has_Boots())
set_rule(world.get_location('Sunken Treasure'), lambda state: state.can_reach('Dam')) set_rule(world.get_location('Sunken Treasure'), lambda state: state.can_reach('Dam'))
set_rule(world.get_entrance('Bat Cave Drop Ledge'), lambda state: state.has('Hammer')) set_rule(world.get_entrance('Bat Cave Drop Ledge'), lambda state: state.has('Hammer'))
@ -96,7 +101,8 @@ def global_rules(world):
set_rule(world.get_entrance('Sanctuary Grave'), lambda state: state.can_lift_rocks()) set_rule(world.get_entrance('Sanctuary Grave'), lambda state: state.can_lift_rocks())
set_rule(world.get_entrance('20 Rupee Cave'), lambda state: state.can_lift_rocks()) set_rule(world.get_entrance('20 Rupee Cave'), lambda state: state.can_lift_rocks())
set_rule(world.get_entrance('50 Rupee Cave'), lambda state: state.can_lift_rocks()) set_rule(world.get_entrance('50 Rupee Cave'), lambda state: state.can_lift_rocks())
set_rule(world.get_entrance('Old Man Cave (West)'), lambda state: state.can_lift_rocks()) set_rule(world.get_entrance('Death Mountain Entrance Rock'), lambda state: state.can_lift_rocks())
set_rule(world.get_entrance('Bumper Cave Entrance Mirror Spot'), lambda state: state.has_Mirror())
set_rule(world.get_entrance('Flute Spot 1'), lambda state: state.has('Ocarina')) set_rule(world.get_entrance('Flute Spot 1'), lambda state: state.has('Ocarina'))
set_rule(world.get_entrance('Lake Hylia Central Island Teleporter'), lambda state: state.can_lift_heavy_rocks()) set_rule(world.get_entrance('Lake Hylia Central Island Teleporter'), lambda state: state.can_lift_heavy_rocks())
set_rule(world.get_entrance('Dark Desert Teleporter'), lambda state: state.has('Ocarina') and state.can_lift_heavy_rocks()) set_rule(world.get_entrance('Dark Desert Teleporter'), lambda state: state.has('Ocarina') and state.can_lift_heavy_rocks())
@ -119,7 +125,6 @@ def global_rules(world):
set_rule(world.get_location('Master Sword Pedestal'), lambda state: state.has('Red Pendant') and state.has('Blue Pendant') and state.has('Green Pendant')) set_rule(world.get_location('Master Sword Pedestal'), lambda state: state.has('Red Pendant') and state.has('Blue Pendant') and state.has('Green Pendant'))
set_rule(world.get_location('Sahasrahla'), lambda state: state.has('Green Pendant')) set_rule(world.get_location('Sahasrahla'), lambda state: state.has('Green Pendant'))
set_rule(world.get_entrance('Agahnims Tower'), lambda state: state.has('Cape') or state.has_beam_sword() or state.has('Beat Agahnim 1')) # barrier gets removed after killing agahnim, relevant for entrance shuffle set_rule(world.get_entrance('Agahnims Tower'), lambda state: state.has('Cape') or state.has_beam_sword() or state.has('Beat Agahnim 1')) # barrier gets removed after killing agahnim, relevant for entrance shuffle
# FIXME: VT has a can_kill_most_things(8) call on Aga Tower's entrance. I think this is supposed to reflect that a better weapon than 10 bombs is needed to reach the two chests in this tower
set_rule(world.get_entrance('Agahnim 1'), lambda state: state.has_sword() and state.has('Small Key (Agahnims Tower)', 2)) set_rule(world.get_entrance('Agahnim 1'), lambda state: state.has_sword() and state.has('Small Key (Agahnims Tower)', 2))
set_rule(world.get_location('Castle Tower - Dark Maze'), lambda state: state.has('Small Key (Agahnims Tower)')) set_rule(world.get_location('Castle Tower - Dark Maze'), lambda state: state.has('Small Key (Agahnims Tower)'))
set_rule(world.get_entrance('Top of Pyramid'), lambda state: state.has('Beat Agahnim 1')) set_rule(world.get_entrance('Top of Pyramid'), lambda state: state.has('Beat Agahnim 1'))
@ -135,10 +140,11 @@ def global_rules(world):
set_rule(world.get_entrance('East Death Mountain (Top)'), lambda state: state.has('Hammer')) set_rule(world.get_entrance('East Death Mountain (Top)'), lambda state: state.has('Hammer'))
set_rule(world.get_location('Catfish'), lambda state: state.can_lift_rocks()) set_rule(world.get_location('Catfish'), lambda state: state.can_lift_rocks())
set_rule(world.get_entrance('Dark World Potion Shop'), lambda state: state.has_Pearl() and (state.can_lift_rocks() or state.has('Hammer') or state.has('Flippers'))) set_rule(world.get_entrance('Northeast Dark World Broken Bridge Pass'), lambda state: state.has_Pearl() and (state.can_lift_rocks() or state.has('Hammer') or state.has('Flippers')))
set_rule(world.get_entrance('East Dark World Broken Bridge Pass'), lambda state: state.has_Pearl() and (state.can_lift_rocks() or state.has('Hammer')))
set_rule(world.get_entrance('South Dark World Bridge'), lambda state: state.has('Hammer') and state.has_Pearl()) set_rule(world.get_entrance('South Dark World Bridge'), lambda state: state.has('Hammer') and state.has_Pearl())
set_rule(world.get_entrance('Bonk Fairy (Dark)'), lambda state: state.has_Pearl() and state.has_Boots()) set_rule(world.get_entrance('Bonk Fairy (Dark)'), lambda state: state.has_Pearl() and state.has_Boots())
set_rule(world.get_entrance('West Dark World Gap'), lambda state: state.has_Pearl() and state.has('Hookshot') and (state.has('Flippers') or state.has('Hammer') or state.can_lift_rocks())) set_rule(world.get_entrance('West Dark World Gap'), lambda state: state.has_Pearl() and state.has('Hookshot'))
set_rule(world.get_entrance('Palace of Darkness'), lambda state: state.has_Pearl()) # kiki needs pearl set_rule(world.get_entrance('Palace of Darkness'), lambda state: state.has_Pearl()) # kiki needs pearl
set_rule(world.get_entrance('Hyrule Castle Ledge Mirror Spot'), lambda state: state.has_Mirror()) set_rule(world.get_entrance('Hyrule Castle Ledge Mirror Spot'), lambda state: state.has_Mirror())
set_rule(world.get_entrance('Hyrule Castle Main Gate'), lambda state: state.has_Mirror()) set_rule(world.get_entrance('Hyrule Castle Main Gate'), lambda state: state.has_Mirror())
@ -153,6 +159,7 @@ def global_rules(world):
set_rule(world.get_entrance('Brewery'), lambda state: state.has_Pearl()) # bomb required set_rule(world.get_entrance('Brewery'), lambda state: state.has_Pearl()) # bomb required
set_rule(world.get_entrance('Thieves Town'), lambda state: state.has_Pearl()) # bunny cannot pull set_rule(world.get_entrance('Thieves Town'), lambda state: state.has_Pearl()) # bunny cannot pull
set_rule(world.get_entrance('Skull Woods First Section Hole (North)'), lambda state: state.has_Pearl()) # bunny cannot lift bush set_rule(world.get_entrance('Skull Woods First Section Hole (North)'), lambda state: state.has_Pearl()) # bunny cannot lift bush
set_rule(world.get_entrance('Skull Woods Second Section Hole'), lambda state: state.has_Pearl()) # bunny cannot lift bush
set_rule(world.get_entrance('Maze Race Mirror Spot'), lambda state: state.has_Mirror()) set_rule(world.get_entrance('Maze Race Mirror Spot'), lambda state: state.has_Mirror())
set_rule(world.get_entrance('Cave 45 Mirror Spot'), lambda state: state.has_Mirror()) set_rule(world.get_entrance('Cave 45 Mirror Spot'), lambda state: state.has_Mirror())
set_rule(world.get_entrance('East Dark World Bridge'), lambda state: state.has_Pearl() and state.has('Hammer')) set_rule(world.get_entrance('East Dark World Bridge'), lambda state: state.has_Pearl() and state.has('Hammer'))
@ -160,11 +167,14 @@ def global_rules(world):
set_rule(world.get_entrance('Lake Hylia Central Island Mirror Spot'), lambda state: state.has_Mirror()) set_rule(world.get_entrance('Lake Hylia Central Island Mirror Spot'), lambda state: state.has_Mirror())
set_rule(world.get_entrance('East Dark World River Pier'), lambda state: state.has_Pearl() and state.has('Flippers')) # ToDo any fake flipper set up? set_rule(world.get_entrance('East Dark World River Pier'), lambda state: state.has_Pearl() and state.has('Flippers')) # ToDo any fake flipper set up?
set_rule(world.get_entrance('Graveyard Ledge Mirror Spot'), lambda state: state.has_Pearl() and state.has_Mirror()) set_rule(world.get_entrance('Graveyard Ledge Mirror Spot'), lambda state: state.has_Pearl() and state.has_Mirror())
set_rule(world.get_entrance('Bumper Cave (Bottom)'), lambda state: state.has_Pearl() and state.can_lift_rocks()) set_rule(world.get_entrance('Bumper Cave Entrance Rock'), lambda state: state.has_Pearl() and state.can_lift_rocks())
set_rule(world.get_entrance('Bumper Cave Ledge Mirror Spot'), lambda state: state.has_Mirror()) set_rule(world.get_entrance('Bumper Cave Ledge Mirror Spot'), lambda state: state.has_Mirror())
set_rule(world.get_entrance('Bat Cave Drop Ledge Mirror Spot'), lambda state: state.has_Pearl() and state.can_lift_heavy_rocks() and state.has_Mirror()) set_rule(world.get_entrance('Bat Cave Drop Ledge Mirror Spot'), lambda state: state.has_Mirror())
set_rule(world.get_entrance('Dark World Hammer Peg Cave'), lambda state: state.has_Pearl() and state.can_lift_heavy_rocks() and state.has('Hammer')) set_rule(world.get_entrance('Dark World Hammer Peg Cave'), lambda state: state.has_Pearl() and state.has('Hammer'))
set_rule(world.get_entrance('Dark World Shop'), lambda state: state.has_Pearl() and state.has('Hammer')) set_rule(world.get_entrance('Village of Outcasts Eastern Rocks'), lambda state: state.has_Pearl() and state.can_lift_heavy_rocks())
set_rule(world.get_entrance('Peg Area Rocks'), lambda state: state.has_Pearl() and state.can_lift_heavy_rocks())
set_rule(world.get_entrance('Village of Outcasts Pegs'), lambda state: state.has_Pearl() and state.has('Hammer'))
set_rule(world.get_entrance('Grassy Lawn Pegs'), lambda state: state.has_Pearl() and state.has('Hammer'))
set_rule(world.get_entrance('Bumper Cave Exit (Top)'), lambda state: state.has('Cape')) set_rule(world.get_entrance('Bumper Cave Exit (Top)'), lambda state: state.has('Cape'))
set_rule(world.get_entrance('Bumper Cave Exit (Bottom)'), lambda state: state.has('Cape') or state.has('Hookshot')) set_rule(world.get_entrance('Bumper Cave Exit (Bottom)'), lambda state: state.has('Cape') or state.has('Hookshot'))
@ -185,20 +195,13 @@ def global_rules(world):
set_rule(world.get_entrance('Fairy Ascension Mirror Spot'), lambda state: state.has_Mirror() and state.has_Pearl()) # need to lift flowers set_rule(world.get_entrance('Fairy Ascension Mirror Spot'), lambda state: state.has_Mirror() and state.has_Pearl()) # need to lift flowers
set_rule(world.get_entrance('Isolated Ledge Mirror Spot'), lambda state: state.has_Mirror()) set_rule(world.get_entrance('Isolated Ledge Mirror Spot'), lambda state: state.has_Mirror())
set_rule(world.get_entrance('Superbunny Cave Exit (Bottom)'), lambda state: False) # Cannot get to bottom exit from top. Just exists for shuffling set_rule(world.get_entrance('Superbunny Cave Exit (Bottom)'), lambda state: False) # Cannot get to bottom exit from top. Just exists for shuffling
set_rule(world.get_location('Spike Cave'), lambda state: set_rule(world.get_location('Spike Cave'), lambda state:
state.has('Hammer') and state.can_lift_rocks() and state.has('Hammer') and state.can_lift_rocks() and
( ((state.has('Cape') and state.can_extend_magic(16, True)) or
( (state.has('Cane of Byrna') and
state.has('Cape') (state.can_extend_magic(12, True) or
and (state.can_extend_magic(16) (state.world.can_take_damage and (state.has_Boots() or state.has_hearts(4))))))
or (state.can_extend_magic(12) and (state.world.can_take_damage or state.has_Boots()))
or (state.can_extend_magic(10) and state.world.can_take_damage and state.has_Boots()))
) or (
state.has('Cane of Byrna')
and (state.can_extend_magic(12)
or (state.can_extend_magic(10) and (state.has_Boots() or state.world.can_take_damage))
or (state.world.can_take_damage and (state.has_Boots() or state.has_hearts(4)))))
)
) )
set_rule(world.get_location('Hookshot Cave - Top Right'), lambda state: state.has('Hookshot')) set_rule(world.get_location('Hookshot Cave - Top Right'), lambda state: state.has('Hookshot'))
@ -257,7 +260,7 @@ def global_rules(world):
set_rule(world.get_entrance('Thieves Town Big Key Door'), lambda state: state.has('Big Key (Thieves Town)')) set_rule(world.get_entrance('Thieves Town Big Key Door'), lambda state: state.has('Big Key (Thieves Town)'))
set_rule(world.get_entrance('Blind Fight'), lambda state: state.has('Small Key (Thieves Town)') and (state.has_blunt_weapon() or state.has('Cane of Somaria') or state.has('Cane of Byrna'))) set_rule(world.get_entrance('Blind Fight'), lambda state: state.has('Small Key (Thieves Town)') and (state.has_blunt_weapon() or state.has('Cane of Somaria') or state.has('Cane of Byrna')))
set_rule(world.get_location('Thieves\' Town - Big Chest'), lambda state: (state.has('Small Key (Thieves Town)') or item_name(state, 'Thieves\' Town - Big Chest') == 'Small Key (Thieves Town)') and state.has('Hammer')) set_rule(world.get_location('Thieves\' Town - Big Chest'), lambda state: (state.has('Small Key (Thieves Town)') or item_name(state, 'Thieves\' Town - Big Chest') == 'Small Key (Thieves Town)') and state.has('Hammer'))
set_always_allow(world.get_location('Thieves\' Town - Big Chest'), lambda state, item: item.name == 'Small Key (Thieves Town)') set_always_allow(world.get_location('Thieves\' Town - Big Chest'), lambda state, item: item.name == 'Small Key (Thieves Town)' and state.has('Hammer'))
set_rule(world.get_location('Thieves\' Town - Attic'), lambda state: state.has('Small Key (Thieves Town)')) set_rule(world.get_location('Thieves\' Town - Attic'), lambda state: state.has('Small Key (Thieves Town)'))
for location in ['Thieves\' Town - Attic', 'Thieves\' Town - Big Chest', 'Thieves\' Town - Blind\'s Cell', 'Thieves Town - Blind']: for location in ['Thieves\' Town - Attic', 'Thieves\' Town - Big Chest', 'Thieves\' Town - Blind\'s Cell', 'Thieves Town - Blind']:
forbid_item(world.get_location(location), 'Big Key (Thieves Town)') forbid_item(world.get_location(location), 'Big Key (Thieves Town)')
@ -316,8 +319,8 @@ def global_rules(world):
set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Left'), lambda state: state.has('Cane of Byrna') or state.has('Cape') or state.has('Mirror Shield')) set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Left'), lambda state: state.has('Cane of Byrna') or state.has('Cape') or state.has('Mirror Shield'))
set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Right'), lambda state: state.has('Cane of Byrna') or state.has('Cape') or state.has('Mirror Shield')) set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Right'), lambda state: state.has('Cane of Byrna') or state.has('Cape') or state.has('Mirror Shield'))
set_rule(world.get_entrance('Turtle Rock (Trinexx)'), lambda state: state.has('Small Key (Turtle Rock)', 4) and state.has('Big Key (Turtle Rock)') and state.has('Cane of Somaria') and state.has('Fire Rod') and state.has('Ice Rod') and set_rule(world.get_entrance('Turtle Rock (Trinexx)'), lambda state: state.has('Small Key (Turtle Rock)', 4) and state.has('Big Key (Turtle Rock)') and state.has('Cane of Somaria') and state.has('Fire Rod') and state.has('Ice Rod') and
(state.has('Hammer') or state.has_beam_sword() or (state.has_sword and state.can_extend_magic(32)))) (state.has('Hammer') or state.has_beam_sword() or (state.has_sword() and state.can_extend_magic(32))))
# TODO: Per VT, possibly allow a regular sword with 4x extended magic (ie. quater magic, or half magic+bottle or 3 bottles)
set_trock_key_rules(world) set_trock_key_rules(world)
set_rule(world.get_entrance('Palace of Darkness Bonk Wall'), lambda state: state.has('Bow')) set_rule(world.get_entrance('Palace of Darkness Bonk Wall'), lambda state: state.has('Bow'))
@ -434,9 +437,9 @@ def no_glitches_rules(world):
add_conditional_lamp('Eastern Palace - Prize', 'Eastern Palace', 'Location') add_conditional_lamp('Eastern Palace - Prize', 'Eastern Palace', 'Location')
if not world.sewer_light_cone: if not world.sewer_light_cone:
add_rule(world.get_location('Sewers - Dark Cross'), lambda state: state.has('Lamp')) add_lamp_requirement(world.get_location('Sewers - Dark Cross'))
add_rule(world.get_entrance('Sewers Back Door'), lambda state: state.has('Lamp')) add_lamp_requirement(world.get_entrance('Sewers Back Door'))
add_rule(world.get_entrance('Throne Room'), lambda state: state.has('Lamp')) add_lamp_requirement(world.get_entrance('Throne Room'))
def open_rules(world): def open_rules(world):
@ -450,7 +453,7 @@ def open_rules(world):
def swordless_rules(world): def swordless_rules(world):
# for the time being swordless mode just inhierits all fixes from open mode. # for the time being swordless mode just inherits all fixes from open mode.
# should there ever be fixes that apply to open mode but not swordless, this # should there ever be fixes that apply to open mode but not swordless, this
# can be revisited. # can be revisited.
open_rules(world) open_rules(world)
@ -470,9 +473,12 @@ def swordless_rules(world):
def standard_rules(world): def standard_rules(world):
for loc in ['Sanctuary','Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle',
'Sewers - Secret Room - Right']:
add_rule(world.get_location(loc), lambda state: state.can_kill_most_things() and state.has('Small Key (Escape)'))
# easiest way to enforce key placement not relevant for open # easiest way to enforce key placement not relevant for open
set_rule(world.get_location('Sewers - Dark Cross'), lambda state: state.can_kill_most_things()) set_rule(world.get_location('Sewers - Dark Cross'), lambda state: state.can_kill_most_things())
add_rule(world.get_entrance('Sewers Door'), lambda state: state.can_kill_most_things())
set_rule(world.get_location('Hyrule Castle - Boomerang Chest'), lambda state: state.can_kill_most_things()) set_rule(world.get_location('Hyrule Castle - Boomerang Chest'), lambda state: state.can_kill_most_things())
set_rule(world.get_location('Hyrule Castle - Zelda\'s Chest'), lambda state: state.can_kill_most_things()) set_rule(world.get_location('Hyrule Castle - Zelda\'s Chest'), lambda state: state.can_kill_most_things())
@ -545,7 +551,7 @@ def set_big_bomb_rules(world):
Normal_LW_entrances = ['Blinds Hideout', Normal_LW_entrances = ['Blinds Hideout',
'Bonk Fairy (Light)', 'Bonk Fairy (Light)',
'Lake Hylia Fairy', 'Lake Hylia Fairy',
'Swamp Fairy', 'Light Hype Fairy',
'Desert Fairy', 'Desert Fairy',
'Chicken House', 'Chicken House',
'Aginahs Cave', 'Aginahs Cave',
@ -574,14 +580,26 @@ def set_big_bomb_rules(world):
'Dam', 'Dam',
'Lumberjack House', 'Lumberjack House',
'Lake Hylia Fortune Teller', 'Lake Hylia Fortune Teller',
'Kakariko Gamble Game'] 'Eastern Palace',
'Kakariko Gamble Game',
'Kakariko Well Cave',
'Bat Cave Cave',
'Elder House (East)',
'Elder House (West)',
'North Fairy Cave',
'Lost Woods Hideout Stump',
'Lumberjack Tree Cave',
'Two Brothers House (East)',
'Sanctuary',
'Hyrule Castle Entrance (South)',
'Hyrule Castle Secret Entrance Stairs']
LW_walkable_entrances = ['Dark Lake Hylia Ledge Fairy', LW_walkable_entrances = ['Dark Lake Hylia Ledge Fairy',
'Dark Lake Hylia Ledge Spike Cave', 'Dark Lake Hylia Ledge Spike Cave',
'Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Hint',
'Mire Shed', 'Mire Shed',
'Dark Desert Hint', 'Dark Desert Hint',
'Dark Desert Fairy', 'Dark Desert Fairy',
'Checkerboard Cave'] 'Misery Mire']
Northern_DW_entrances = ['Brewery', Northern_DW_entrances = ['Brewery',
'C-Shaped House', 'C-Shaped House',
'Chest Game', 'Chest Game',
@ -591,88 +609,238 @@ def set_big_bomb_rules(world):
'Fortune Teller (Dark)', 'Fortune Teller (Dark)',
'Dark World Shop', 'Dark World Shop',
'Dark World Lumberjack Shop', 'Dark World Lumberjack Shop',
'Graveyard Cave'] 'Thieves Town',
'Skull Woods First Section Door',
'Skull Woods Second Section Door (East)']
Southern_DW_entrances = ['Hype Cave', Southern_DW_entrances = ['Hype Cave',
'Bonk Fairy (Dark)', 'Bonk Fairy (Dark)',
'Archery Game', 'Archery Game',
'Big Bomb Shop', 'Big Bomb Shop',
'Dark Lake Hylia Shop', 'Dark Lake Hylia Shop',
'Cave 45'] 'Swamp Palace']
Isolated_DW_entrances = ['Spike Cave', Isolated_DW_entrances = ['Spike Cave',
'Cave Shop (Dark Death Mountain)', 'Cave Shop (Dark Death Mountain)',
'Dark Death Mountain Fairy', 'Dark Death Mountain Fairy',
'Mimic Cave'] 'Mimic Cave',
'Skull Woods Second Section Door (West)',
'Skull Woods Final Section',
'Ice Palace',
'Turtle Rock',
'Dark Death Mountain Ledge (West)',
'Dark Death Mountain Ledge (East)',
'Bumper Cave (Top)',
'Superbunny Cave (Top)',
'Superbunny Cave (Bottom)',
'Hookshot Cave',
'Ganons Tower',
'Turtle Rock Isolated Ledge Entrance',
'Hookshot Cave Back Entrance']
Isolated_LW_entrances = ['Capacity Upgrade', Isolated_LW_entrances = ['Capacity Upgrade',
'Hookshot Fairy'] 'Tower of Hera',
'Death Mountain Return Cave (West)',
'Paradox Cave (Top)',
'Fairy Ascension Cave (Top)',
'Spiral Cave',
'Desert Palace Entrance (East)']
West_LW_DM_entrances = ['Old Man Cave (East)',
'Old Man House (Bottom)',
'Old Man House (Top)',
'Death Mountain Return Cave (East)',
'Spectacle Rock Cave Peak',
'Spectacle Rock Cave',
'Spectacle Rock Cave (Bottom)']
East_LW_DM_entrances = ['Paradox Cave (Bottom)',
'Paradox Cave (Middle)',
'Hookshot Fairy',
'Spiral Cave (Bottom)']
Mirror_from_SDW_entrances = ['Two Brothers House (West)',
'Cave 45']
Castle_ledge_entrances = ['Hyrule Castle Entrance (West)',
'Hyrule Castle Entrance (East)',
'Agahnims Tower']
Desert_mirrorable_ledge_entrances = ['Desert Palace Entrance (West)',
'Desert Palace Entrance (North)',
'Desert Palace Entrance (South)',
'Checkerboard Cave',]
set_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.can_reach('East Dark World', 'Region') and state.can_reach('Big Bomb Shop', 'Region') and state.has('Crystal 5') and state.has('Crystal 6')) set_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.can_reach('East Dark World', 'Region') and state.can_reach('Big Bomb Shop', 'Region') and state.has('Crystal 5') and state.has('Crystal 6'))
#crossing peg bridge starting from the southern dark world
def cross_peg_bridge(state):
return state.has('Hammer') and state.has_Pearl()
# returning via the eastern and southern teleporters needs the same items, so we use the southern teleporter for out routing.
# crossing preg bridge already requires hammer so we just add the gloves to the requirement
def southern_teleporter(state):
return state.can_lift_rocks() and cross_peg_bridge(state)
# the basic routes assume you can reach eastern light world with the bomb.
# you can then use the southern teleporter, or (if you have beaten Aga1) the hyrule castle gate warp
def basic_routes(state):
return southern_teleporter(state) or state.can_reach('Top of Pyramid', 'Entrance')
# Key for below abbreviations:
# P = pearl
# A = Aga1
# H = hammer
# M = Mirror
# G = Glove
if bombshop_entrance.name in Normal_LW_entrances: if bombshop_entrance.name in Normal_LW_entrances:
#1. Enter via gate: Needs Aga1 #1. basic routes
#2. south hyrule teleporter and cross peg bridge: Hammer and moon pearl #2. Can reach Eastern dark world some other way, mirror, get bomb, return to mirror spot, walk to pyramid: Needs mirror
#3. Can reach Eastern dark world some other way, mirror, get bomb, return to mirror spot, walk to pyramid: Needs mirror # -> M or BR
# -> A or (H and P)) or (M) add_rule(world.get_entrance('Pyramid Fairy'), lambda state: basic_routes(state) or state.has_Mirror())
add_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.can_reach('Top of Pyramid', 'Entrance') or (state.has('Hammer') and state.has_Pearl()) or state.has_Mirror())
elif bombshop_entrance.name in LW_walkable_entrances: elif bombshop_entrance.name in LW_walkable_entrances:
#1. Mirror then gate: Needs mirror and Aga1 #1. Mirror then basic routes
#2. Mirror then go to south hyrule teleporter and cross peg bridge: Needs Mirror and Hammer and moon pearl # -> M and BR
# -> M and (A or (H and P)) add_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.has_Mirror() and basic_routes(state))
add_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.has_Mirror() and (state.can_reach('Top of Pyramid', 'Entrance') or (state.has('Hammer') and state.has_Pearl())))
elif bombshop_entrance.name in Northern_DW_entrances: elif bombshop_entrance.name in Northern_DW_entrances:
#1. Mirror and enter via gate: Need mirror and Aga1 #1. Mirror and basic routes
#2. Mirror and enter via south hyrule teleporter: Need mirror and hammer and moon pearl #2. Go to south DW and then cross peg bridge: Need Mitts and hammer and moon pearl
# -> (Mitts and CPB) or (M and BR)
add_rule(world.get_entrance('Pyramid Fairy'), lambda state: (state.can_lift_heavy_rocks() and cross_peg_bridge(state)) or (state.has_Mirror() and basic_routes(state)))
elif bombshop_entrance.name == 'Bumper Cave (Bottom)':
#1. Mirror and Lift rock and basic_routes
#2. Mirror and Flute and basic routes (can make difference if accessed via insanity or w/ mirror from connector, and then via hyrule castle gate, because no gloves are needed in that case)
#3. Go to south DW and then cross peg bridge: Need Mitts and hammer and moon pearl #3. Go to south DW and then cross peg bridge: Need Mitts and hammer and moon pearl
# -> (Mitts and P and H) or (M and (A or (H and P))) # -> (Mitts and CPB) or (((G or Flute) and M) and BR))
add_rule(world.get_entrance('Pyramid Fairy'), lambda state: (state.can_lift_heavy_rocks() and state.has_Pearl() and state.has('Hammer')) or (state.has_Mirror() and (state.can_reach('Top of Pyramid', 'Entrance') or (state.has('Hammer') and state.has_Pearl())))) add_rule(world.get_entrance('Pyramid Fairy'), lambda state: (state.can_lift_heavy_rocks() and cross_peg_bridge(state)) or (((state.can_lift_rocks() or state.has('Ocarina')) and state.has_Mirror()) and basic_routes(state)))
elif bombshop_entrance.name in Southern_DW_entrances: elif bombshop_entrance.name in Southern_DW_entrances:
#1. Mirror and enter via gate: Need mirror and Aga1 #1. Mirror and enter via gate: Need mirror and Aga1
#2. cross peg bridge: Need hammer and moon pearl #2. cross peg bridge: Need hammer and moon pearl
# -> (H and P) or (M and A) # -> CPB or (M and A)
add_rule(world.get_entrance('Pyramid Fairy'), lambda state: (state.has('Hammer') and state.has_Pearl()) or (state.has_Mirror() and state.can_reach('Top of Pyramid', 'Entrance'))) add_rule(world.get_entrance('Pyramid Fairy'), lambda state: cross_peg_bridge(state) or (state.has_Mirror() and state.can_reach('Top of Pyramid', 'Entrance')))
elif bombshop_entrance.name in Isolated_DW_entrances: elif bombshop_entrance.name in Isolated_DW_entrances:
# 1. mirror then flute then enter via gate: Needs mirror and flute and Aga 1 # 1. mirror then flute then basic routes
# 2. mirror then flute then enter via south hyrule teleporter: Needs mirror and Flute and hammer and moon pearl # -> M and Flute and BR
# -> M and Flute and (A or (H and P)) add_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.has_Mirror() and state.has('Ocarina') and basic_routes(state))
add_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.has_Mirror() and state.has('Ocarina') and (state.can_reach('Top of Pyramid', 'Entrance') or (state.has('Hammer') and state.has_Pearl())))
elif bombshop_entrance.name in Isolated_LW_entrances: elif bombshop_entrance.name in Isolated_LW_entrances:
# 1. flute then enter via gate: Needs flute and Aga 1 # 1. flute then basic routes
# 2. flute then enter via south hyrule teleporter: Needs Flute and hammer and moon pearl
# Prexisting mirror spot is not permitted, because mirror might have been needed to reach these isolated locations. # Prexisting mirror spot is not permitted, because mirror might have been needed to reach these isolated locations.
# -> Flute and (A or (H and P)) # -> Flute and BR
add_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.has('Ocarina') and (state.can_reach('Top of Pyramid', 'Entrance') or (state.has('Hammer') and state.has_Pearl()))) add_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.has('Ocarina') and basic_routes(state))
elif bombshop_entrance.name in West_LW_DM_entrances:
# 1. flute then basic routes or mirror
# Prexisting mirror spot is permitted, because flute can be used to reach west DM directly.
# -> Flute and (M or BR)
add_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.has('Ocarina') and (state.has_Mirror() or basic_routes(state)))
elif bombshop_entrance.name in East_LW_DM_entrances:
# 1. flute then basic routes or mirror and hookshot
# Prexisting mirror spot is permitted, because flute can be used to reach west DM directly and then east DM via Hookshot
# -> Flute and ((M and Hookshot) or BR)
add_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.has('Ocarina') and ((state.has_Mirror() and state.has('Hookshot')) or basic_routes(state)))
elif bombshop_entrance.name == 'Fairy Ascension Cave (Bottom)':
# Same as East_LW_DM_entrances except navigation without BR requires Mitts
# -> Flute and ((M and Hookshot and Mitts) or BR)
add_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.has('Ocarina') and ((state.has_Mirror() and state.has('Hookshot') and state.can_lift_heavy_rocks()) or basic_routes(state)))
elif bombshop_entrance.name in Castle_ledge_entrances:
# 1. mirror on pyramid to castle ledge, grab bomb, return through mirror spot: Needs mirror
# 2. flute then basic routes
# -> M or (Flute and BR)
add_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.has_Mirror() or (state.has('Ocarina') and basic_routes(state)))
elif bombshop_entrance.name in Desert_mirrorable_ledge_entrances:
# Cases when you have mire access: Mirror to reach locations, return via mirror spot, move to center of desert, mirror anagin and:
# 1. Have mire access, Mirror to reach locations, return via mirror spot, move to center of desert, mirror again and then basic routes
# 2. flute then basic routes
# -> (Mire access and M) or Flute) and BR
add_rule(world.get_entrance('Pyramid Fairy'), lambda state: ((state.can_reach('Dark Desert', 'Region') and state.has_Mirror()) or state.has('Ocarina')) and basic_routes(state))
elif bombshop_entrance.name == 'Old Man Cave (West)':
# 1. Lift rock then basic_routes
# 2. flute then basic_routes
# -> (Flute or G) and BR
add_rule(world.get_entrance('Pyramid Fairy'), lambda state: (state.has('Ocarina') or state.can_lift_rocks()) and basic_routes(state))
elif bombshop_entrance.name == 'Graveyard Cave':
# 1. flute then basic routes
# 2. (has west dark world access) use existing mirror spot (required Pearl), mirror again off ledge
# -> (Flute or (M and P and West Dark World access) and BR
add_rule(world.get_entrance('Pyramid Fairy'), lambda state: (state.has('Ocarina') or (state.can_reach('West Dark World', 'Region') and state.has_Pearl() and state.has_Mirror())) and basic_routes(state))
elif bombshop_entrance.name in Mirror_from_SDW_entrances:
# 1. flute then basic routes
# 2. (has South dark world access) use existing mirror spot, mirror again off ledge
# -> (Flute or (M and South Dark World access) and BR
add_rule(world.get_entrance('Pyramid Fairy'), lambda state: (state.has('Ocarina') or (state.can_reach('South Dark World', 'Region') and state.has_Mirror())) and basic_routes(state))
elif bombshop_entrance.name == 'Dark World Potion Shop': elif bombshop_entrance.name == 'Dark World Potion Shop':
# 1. walk down by lifting rock: needs gloves and pearl` # 1. walk down by lifting rock: needs gloves and pearl`
# 2. walk down by hammering peg: needs hammer and pearl # 2. walk down by hammering peg: needs hammer and pearl
# 3. mirror and eneter via gate: needs Mirror and Aga1 # 3. mirror and basic routes
# (south hyrule teleporter would be redundant with #2) # -> (P and (H or Gloves)) or (M and BR)
# -> P and (H or H) or (M and A) add_rule(world.get_entrance('Pyramid Fairy'), lambda state: (state.has_Pearl() and (state.has('Hammer') or state.can_lift_rocks())) or (state.has_Mirror() and basic_routes(state)))
add_rule(world.get_entrance('Pyramid Fairy'), lambda state: (state.has_Pearl() and (state.has('Hammer') or state.can_lift_rocks())) or (state.has_Mirror() and state.can_reach('Top of Pyramid', 'Entrance')))
elif bombshop_entrance.name == 'Kings Grave': elif bombshop_entrance.name == 'Kings Grave':
# same as the Normal_LW_entrances case except that the pre-existing mirror is only possible if you have mitts # same as the Normal_LW_entrances case except that the pre-existing mirror is only possible if you have mitts
# (because otherwise mirror was used to reach the grave, so would cancel a pre-existing mirror spot) # (because otherwise mirror was used to reach the grave, so would cancel a pre-existing mirror spot)
# -> A or (H and P) or (M and Mitts) # to account for insanity, must consider a way to escape without a cave for basic_routes
add_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.can_reach('Top of Pyramid', 'Entrance') or (state.has('Hammer') and state.has_Pearl()) or (state.can_lift_heavy_rocks() and state.has_Mirror())) # -> (M and Mitts) or ((Mitts or Flute or (M and P and West Dark World access)) and BR)
add_rule(world.get_entrance('Pyramid Fairy'), lambda state: (state.can_lift_heavy_rocks() and state.has_Mirror()) or ((state.can_lift_heavy_rocks() or state.has('Ocarina') or (state.can_reach('West Dark World', 'Region') and state.has_Pearl() and state.has_Mirror())) and basic_routes(state)))
def set_bunny_rules(world): def set_bunny_rules(world):
# regions for the extis of multi-entrace caves/drops that bunny cannot pass # regions for the exits of multi-entrace caves/drops that bunny cannot pass
# Note spiral cave may be technically passible, but it would be too absurd to require since OHKO mode is a thing. # Note spiral cave may be technically passible, but it would be too absurd to require since OHKO mode is a thing.
bunny_impassable_caves = ['Bumper Cave', 'Two Brothers House', 'Hookshot Cave', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Left)', 'Skull Woods First Section (Top)', 'Turtle Rock (Entrance)', 'Turtle Rock (Second Section)', 'Turtle Rock (Big Chest)', 'Skull Woods Second Section (Drop)', bunny_impassable_caves = ['Bumper Cave', 'Two Brothers House', 'Hookshot Cave', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Left)', 'Skull Woods First Section (Top)', 'Turtle Rock (Entrance)', 'Turtle Rock (Second Section)', 'Turtle Rock (Big Chest)', 'Skull Woods Second Section (Drop)',
'Turtle Rock (Eye Bridge)', 'Sewers', 'Pyramid', 'Spiral Cave (Top)'] 'Turtle Rock (Eye Bridge)', 'Sewers', 'Pyramid', 'Spiral Cave (Top)', 'Desert Palace Main (Inner)']
bunny_accessible_locations = ['Link\'s Uncle', 'Sahasrahla', 'Sick Kid', 'Lost Woods Hideout', 'Lumberjack Tree', 'Checkerboard Cave', 'Potion Shop', 'Spectacle Rock Cave', 'Pyramid', 'Hype Cave - Generous Guy', 'Peg Cave', 'Bumper Cave Ledge'] bunny_accessible_locations = ['Link\'s Uncle', 'Sahasrahla', 'Sick Kid', 'Lost Woods Hideout', 'Lumberjack Tree', 'Checkerboard Cave', 'Potion Shop', 'Spectacle Rock Cave', 'Pyramid', 'Hype Cave - Generous Guy', 'Peg Cave', 'Bumper Cave Ledge']
# Add pearl requirements for bunny-impassible caves if they occur in the dark world if world.get_region('Dam').is_dark_world:
# if Dam is is dark world, then it is required to have the pearl to get the sunken item
add_rule(world.get_location('Sunken Treasure'), lambda state: state.has_Pearl())
# similarly we need perl to get across the swamp palace moat
add_rule(world.get_entrance('Swamp Palace Moat'), lambda state: state.has_Pearl())
def path_to_access_rule(path, entrance):
return lambda state: state.can_reach(entrance) and all(rule(state) for rule in path)
def options_to_access_rule(options):
return lambda state: any(rule(state) for rule in options)
def get_rule_to_add(region):
if not region.is_light_world:
return lambda state: state.has_Pearl()
# in this case we are mixed region.
# we collect possible options.
# The base option is having the moon pearl
possible_options = [lambda state: state.has_Pearl()]
# We will search entrances recursively until we find
# one that leads to an exclusively light world region
# for each such entrance a new option ios aded that consist of:
# a) being able to reach it, and
# b) being able to access all entrances from there to `region`
seen = set([region])
queue = collections.deque([(region, [])])
while queue:
(current, path) = queue.popleft()
for entrance in current.entrances:
new_region = entrance.parent_region
if new_region in seen:
continue
new_path = path + [entrance.access_rule]
seen.add(new_region)
if not new_region.is_light_world:
continue # we don't care about pure dark world entrances
if new_region.is_dark_world:
queue.append((new_region, new_path))
else:
# we have reached pure light world, so we have a new possible option
possible_options.append(path_to_access_rule(new_path, entrance))
return options_to_access_rule(possible_options)
# Add requirements for bunny-impassible caves if they occur in the dark world
for region in [world.get_region(name) for name in bunny_impassable_caves]: for region in [world.get_region(name) for name in bunny_impassable_caves]:
if region.is_light_world: if not region.is_dark_world:
continue continue
rule = get_rule_to_add(region)
for exit in region.exits: for exit in region.exits:
add_rule(exit, lambda state: state.has_Pearl()) add_rule(exit, rule)
# Add a moon pearl requirement for all locations that are actually in the dark world, except those available to the bunny # Add requirements for all locations that are actually in the dark world, except those available to the bunny
for location in world.get_locations(): for location in world.get_locations():
if not location.parent_region.is_light_world: if location.parent_region.is_dark_world:
if location.name in bunny_accessible_locations: if location.name in bunny_accessible_locations:
continue continue
add_rule(location, lambda state: state.has_Pearl()) add_rule(location, get_rule_to_add(location.parent_region))

File diff suppressed because one or more lines are too long