Merge branch 'pull/23'

# Conflicts:
#	BaseClasses.py
#	EntranceShuffle.py
#	ItemList.py
#	Mystery.py
#	Rules.py
This commit is contained in:
Fabian Dill 2020-04-08 15:07:19 +02:00
commit 95d51d48bf
12 changed files with 534 additions and 96 deletions

View File

@ -544,6 +544,31 @@ class CollectionState(object):
def has_turtle_rock_medallion(self, player: int) -> bool:
return self.has(self.world.required_medallions[player][1], player)
def can_boots_clip_lw(self, player):
if self.world.mode[player] == 'inverted':
return self.has_Boots(player) and self.has_Pearl(player)
return self.has_Boots(player)
def can_boots_clip_dw(self, player):
if self.world.mode[player] != 'inverted':
return self.has_Boots(player) and self.has_Pearl(player)
return self.has_Boots(player)
def can_get_glitched_speed_lw(self, player):
rules = [self.has_Boots(player), any([self.has('Hookshot', player), self.has_sword(player)])]
if self.world.mode[player] == 'inverted':
rules.append(self.has_Pearl(player))
return all(rules)
def can_superbunny_mirror_with_sword(self, player):
return self.has_Mirror(player) and self.has_sword(player)
def can_get_glitched_speed_dw(self, player):
rules = [self.has_Boots(player), any([self.has('Hookshot', player), self.has_sword(player)])]
if self.world.mode[player] != 'inverted':
rules.append(self.has_Pearl(player))
return all(rules)
def collect(self, item: Item, event=False, location=None):
if location:
self.locations_checked.add(location)

View File

@ -29,14 +29,17 @@ def parse_arguments(argv, no_defaults=False):
parser = argparse.ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument('--create_spoiler', help='Output a Spoiler File', action='store_true')
parser.add_argument('--logic', default=defval('noglitches'), const='noglitches', nargs='?', choices=['noglitches', 'minorglitches', 'nologic'],
parser.add_argument('--logic', default=defval('noglitches'), const='noglitches', nargs='?', choices=['noglitches', 'minorglitches', 'owglitches', 'nologic'],
help='''\
Select Enforcement of Item Requirements. (default: %(default)s)
No Glitches:
Minor Glitches: May require Fake Flippers, Bunny Revival
and Dark Room Navigation.
Overworld Glitches: May require overworld glitches. Starts with
boots.
No Logic: Distribute items without regard for
item requirements.
item requirements. Starts with
boots
''')
parser.add_argument('--mode', default=defval('open'), const='open', nargs='?', choices=['standard', 'open', 'inverted'],
help='''\

View File

@ -2880,6 +2880,8 @@ mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia Central
('East Dark World River Pier', 'East Dark World'),
('West Dark World Gap', 'West Dark World'),
('East Dark World Broken Bridge Pass', 'East Dark World'),
('Catfish Exit Rock', 'Northeast Dark World'),
('Catfish Entrance Rock', 'Catfish'),
('Northeast Dark World Broken Bridge Pass', 'Northeast Dark World'),
('Bumper Cave Entrance Rock', 'Bumper Cave Entrance'),
('Bumper Cave Entrance Drop', 'West Dark World'),
@ -2922,6 +2924,7 @@ mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia Central
('Spiral Cave Mirror Spot', 'Spiral Cave Ledge'),
('Mimic Cave Mirror Spot', 'Mimic Cave Ledge'),
('Cave 45 Mirror Spot', 'Cave 45 Ledge'),
('Bombos Tablet Mirror Spot', 'Bombos Tablet Ledge'),
('Graveyard Ledge Mirror Spot', 'Graveyard Ledge'),
('Swamp Palace Moat', 'Swamp Palace (First Room)'),
@ -2984,14 +2987,48 @@ mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia Central
('Ganons Tower Moldorm Door', 'Ganons Tower (Moldorm)'),
('Ganons Tower Moldorm Gap', 'Agahnim 2'),
('Ganon Drop', 'Bottom of Pyramid'),
('Pyramid Drop', 'East Dark World')
('Pyramid Drop', 'East Dark World'),
('Light World DMA Clip Spot', 'Death Mountain'),
('Hera Ascent', 'Death Mountain (Top)'),
('Spectacle Rock Clip Spot', 'Spectacle Rock'),
('Death Mountain Return Ledge Clip Spot', 'Death Mountain Return Ledge'),
('Death Mountain Glitched Bridge', 'East Death Mountain (Top)'),
('Floating Island Clip Spot', 'Death Mountain Floating Island (Light World)'),
('Dark Death Mountain Ledge Clip Spot', 'Dark Death Mountain Ledge'),
('Zora Descent Clip Spot', 'Zoras River'),
('Graveyard Ledge Clip Spot', 'Graveyard Ledge'),
('Graveyard Ledge Clip Spot', 'Kings Grave Area'),
('Desert Northern Cliffs', 'Desert Ledge'),
('Desert Northern Cliffs', 'Desert Ledge (Northeast)'),
('Desert Northern Cliffs', 'Desert Palace Entrance (North) Spot'),
('Lake Hylia Island Clip Spot', 'Lake Hylia Island'),
('Dark World DMA Clip Spot', 'Dark Death Mountain (West Bottom)'),
('Ganons Tower Ascent', 'Dark Death Mountain (Top)'),
('Bumper Cave Ledge Clip Spot', 'Bumper Cave Ledge'),
('Dark Death Mountain Glitched Bridge', 'Dark Death Mountain (Top)'),
('Dark Death Mountain Bunny Descent Mirror Spot', 'Dark Death Mountain Bunny Descent Area'),
('Catfish Descent', 'Catfish'),
('Dark Death Mountain Offset Mirror', 'East Dark World'),
('Hammer Pegs River Clip Spot', 'Hammer Peg Area'),
('Dark Lake Hylia Ledge Clip Spot', 'Dark Lake Hylia Ledge'),
('Dark Desert Cliffs Clip Spot', 'Dark Desert'),
('Bumper Cave Ledge Clip Spot', 'Bumper Cave Entrance'),
('Death Mountain Return Ledge Clip Spot', 'Death Mountain Entrance'),
('West Dark World Bunny Descent', 'West Dark World'),
('Dark Death Mountain (East Bottom) Jump', 'Dark Death Mountain (East Bottom)'),
('Bat Cave River Clip Spot', 'Bat Cave Drop Ledge'),
('Turtle Rock (Top) Clip Spot', 'Turtle Rock (Top)'),
('Dark Death Mountain Descent', 'West Dark World'),
('Death Mountain Descent', 'Light World'),
('Bombos Tablet Clip Spot', 'Bombos Tablet Ledge'),
('Cave 45 Clip Spot', 'Cave 45 Ledge'),
]
inverted_mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia Central Island'),
('Lake Hylia Island Pier', 'Lake Hylia Island'),
('Lake Hylia Warp', 'Northeast Light World'),
('Northeast Light World Warp', 'Light World'),
('Zoras River', 'Zoras River'),
('Zoras River', 'Zoras River'),('Graveyard Ledge Clip Spot', 'Kings Grave Area'),
('Kings Grave Outer Rocks', 'Kings Grave Area'),
('Kings Grave Inner Rocks', 'Light World'),
('Kakariko Well (top to bottom)', 'Kakariko Well (bottom)'),
@ -3047,6 +3084,8 @@ inverted_mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia
('West Dark World Gap', 'West Dark World'),
('East Dark World Broken Bridge Pass', 'East Dark World'),
('Northeast Dark World Broken Bridge Pass', 'Northeast Dark World'),
('Catfish Exit Rock', 'Northeast Dark World'),
('Catfish Entrance Rock', 'Catfish'),
('Bumper Cave Entrance Rock', 'Bumper Cave Entrance'),
('Bumper Cave Entrance Drop', 'West Dark World'),
('Bumper Cave Ledge Drop', 'West Dark World'),
@ -3201,7 +3240,32 @@ inverted_mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia
('Bush Covered Lawn Mirror Spot', 'Dark Grassy Lawn'),
('Bomb Hut Inner Bushes', 'Light World'),
('Bomb Hut Outer Bushes', 'Bomb Hut Area'),
('Bomb Hut Mirror Spot', 'West Dark World')]
('Bomb Hut Mirror Spot', 'West Dark World'),
('Light World DMA Clip Spot', 'Death Mountain'),
('Hera Ascent', 'Death Mountain (Top)'),
('Death Mountain Return Ledge Clip Spot', 'Death Mountain Return Ledge'),
('Death Mountain Glitched Bridge', 'East Death Mountain (Top)'),
('Dark Death Mountain Ledge Clip Spot', 'Dark Death Mountain Ledge'),
('Zora Descent Clip Spot', 'Zoras River'),
('Desert Northern Cliffs', 'Desert Ledge'),
('Desert Northern Cliffs', 'Desert Palace Entrance (North) Spot'),
('Lake Hylia Island Clip Spot', 'Lake Hylia Island'),
('Dark World DMA Clip Spot', 'Dark Death Mountain'),
('Bumper Cave Ledge Clip Spot', 'Bumper Cave Ledge'),
('Catfish Descent', 'Catfish'),
('Death Mountain Offset Mirror', 'Light World'),
('Hammer Pegs River Clip Spot', 'Hammer Peg Area'),
('Dark Lake Hylia Ledge Clip Spot', 'Dark Lake Hylia Ledge'),
('Dark Desert Cliffs Clip Spot', 'Dark Desert'),
('Bumper Cave Ledge Clip Spot', 'Bumper Cave Entrance'),
('Death Mountain Return Ledge Clip Spot', 'Death Mountain Entrance'),
('Light World Bunny Descent', 'Light World'),
('East Death Mountain (Bottom) Jump', 'East Death Mountain (Bottom)'),
('Bat Cave River Clip Spot', 'Bat Cave Drop Ledge'),
('Turtle Rock (Top) Clip Spot', 'Turtle Rock (Top)'),
('Dark Death Mountain Descent', 'West Dark World'),
('Death Mountain Descent', 'Light World'),
('Death Mountain Bunny Descent Mirror Spot', 'Death Mountain Bunny Descent Area')]
# non-shuffled entrance links
default_connections = [('Waterfall of Wishing', 'Waterfall of Wishing'),
("Blinds Hideout", "Blinds Hideout"),

View File

@ -227,7 +227,7 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
# fill in gtower locations with trash first
for player in range(1, world.players + 1):
if not gftower_trash or not world.ganonstower_vanilla[player]:
if not gftower_trash or not world.ganonstower_vanilla[player] or world.logic[player] == 'owglitches':
continue
gftower_trash_count = (random.randint(15, 50) if world.goal[player] == 'triforcehunt' else random.randint(0, 15))

2
Gui.py
View File

@ -228,7 +228,7 @@ def guiMain(args=None):
logicFrame = Frame(drowDownFrame)
logicVar = StringVar()
logicVar.set('noglitches')
logicOptionMenu = OptionMenu(logicFrame, logicVar, 'noglitches', 'minorglitches', 'nologic')
logicOptionMenu = OptionMenu(logicFrame, logicVar, 'noglitches', 'minorglitches', 'owglitches', 'nologic')
logicOptionMenu.pack(side=RIGHT)
logicLabel = Label(logicFrame, text='Game logic')
logicLabel.pack(side=LEFT)

View File

@ -18,7 +18,8 @@ def create_inverted_regions(world, player):
'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',
'East Dark World Mirror Spot', 'West Dark World Mirror Spot', 'South Dark World Mirror Spot', 'Cave 45', 'Checkerboard Cave', 'Mire Mirror Spot', 'Hammer Peg Area Mirror Spot',
'Shopping Mall Mirror Spot', 'Skull Woods Mirror Spot', 'Inverted Pyramid Entrance','Hyrule Castle Entrance (South)', 'Secret Passage Outer Bushes', 'Bush Covered Lawn Outer Bushes',
'Potion Shop Outer Bushes', 'Graveyard Cave Outer Bushes', 'Bomb Hut Outer Bushes']),
'Potion Shop Outer Bushes', 'Graveyard Cave Outer Bushes', 'Bomb Hut Outer Bushes', 'Light World DMA Clip Spot', 'Death Mountain Return Ledge Clip Spot', 'Bat Cave River Clip Spot',
'Desert Northern Cliffs', 'Lake Hylia Island Clip Spot']),
create_lw_region(player, 'Bush Covered Lawn', None, ['Bush Covered House', 'Bush Covered Lawn Inner Bushes', 'Bush Covered Lawn Mirror Spot']),
create_lw_region(player, 'Bomb Hut Area', None, ['Light World Bomb Hut', 'Bomb Hut Inner Bushes', 'Bomb Hut Mirror Spot']),
create_lw_region(player, 'Hyrule Castle Secret Entrance Area', None, ['Hyrule Castle Secret Entrance Stairs', 'Secret Passage Inner Bushes']),
@ -125,7 +126,8 @@ def create_inverted_regions(world, player):
create_cave_region(player, 'Old Man House', 'a connector', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']),
create_cave_region(player, 'Old Man House Back', 'a connector', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']),
create_lw_region(player, '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 Mirror Spot']),
'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)', 'Death Mountain Mirror Spot', 'Death Mountain Bunny Descent Mirror Spot',
'Death Mountain Offset Mirror', 'Hera Ascent', 'Death Mountain Glitched Bridge', 'Death Mountain Descent', 'Graveyard Ledge Clip Spot']),
create_cave_region(player, 'Death Mountain Return Cave', 'a connector', None, ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave Exit (East)']),
create_lw_region(player, 'Death Mountain Return Ledge', None, ['Death Mountain Return Ledge Drop', 'Death Mountain Return Cave (West)', 'Bumper Cave Ledge Mirror Spot']),
create_cave_region(player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'], ['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']),
@ -146,7 +148,7 @@ def create_inverted_regions(world, player):
create_cave_region(player, 'Paradox Cave', 'a connector', None, ['Paradox Cave Exit (Middle)', 'Paradox Cave Exit (Top)', 'Paradox Cave Drop']),
create_cave_region(player, 'Light World Death Mountain Shop', 'a common shop'),
create_lw_region(player, 'East Death Mountain (Top)', ['Floating Island'], ['Paradox Cave (Top)', 'Death Mountain (Top)', 'Spiral Cave Ledge Access', 'East Death Mountain Drop', 'East Death Mountain Mirror Spot (Top)', 'Fairy Ascension Ledge Access', 'Mimic Cave Ledge Access',
'Floating Island Mirror Spot']),
'Floating Island Mirror Spot', 'Zora Descent Clip Spot']),
create_lw_region(player, 'Spiral Cave Ledge', None, ['Spiral Cave', 'Spiral Cave Ledge Drop', 'Dark Death Mountain Ledge Mirror Spot (West)']),
create_lw_region(player, 'Mimic Cave Ledge', None, ['Mimic Cave', 'Mimic Cave Ledge Drop', 'Dark Death Mountain Ledge Mirror Spot (East)']),
create_cave_region(player, 'Spiral Cave (Top)', 'a connector', ['Spiral Cave'], ['Spiral Cave (top to bottom)', 'Spiral Cave Exit (Top)']),
@ -163,8 +165,10 @@ def create_inverted_regions(world, player):
create_dungeon_region(player, 'Tower of Hera (Top)', 'Tower of Hera', ['Tower of Hera - Compass Chest', 'Tower of Hera - Big Chest', 'Tower of Hera - Boss', 'Tower of Hera - Prize']),
create_dw_region(player, 'East Dark World', ['Pyramid'], ['Pyramid Fairy', 'South Dark World Bridge', 'Palace of Darkness', 'Dark Lake Hylia Drop (East)',
'Dark Lake Hylia Fairy', 'Palace of Darkness Hint', 'East Dark World Hint', 'Northeast Dark World Broken Bridge Pass', 'East Dark World Teleporter', 'EDW Flute']),
create_dw_region(player, 'Northeast Dark World', ['Catfish'], ['West Dark World Gap', 'Dark World Potion Shop', 'East Dark World Broken Bridge Pass', 'NEDW Flute', 'Dark Lake Hylia Teleporter']),
'Dark Lake Hylia Fairy', 'Palace of Darkness Hint', 'East Dark World Hint', 'Northeast Dark World Broken Bridge Pass', 'East Dark World Teleporter', 'EDW Flute',
'Hammer Pegs River Clip Spot', 'Dark Lake Hylia Ledge Clip Spot', 'Dark Desert Cliffs Clip Spot']),
create_dw_region(player, 'Catfish', ['Catfish'], ['Catfish Exit Rock']),
create_dw_region(player, 'Northeast Dark World', None, ['West Dark World Gap', 'Dark World Potion Shop', 'East Dark World Broken Bridge Pass', 'NEDW Flute', 'Dark Lake Hylia Teleporter', 'Catfish Entrance Rock']),
create_cave_region(player, 'Palace of Darkness Hint', 'a storyteller'),
create_cave_region(player, 'East Dark World Hint', 'a storyteller'),
create_dw_region(player, 'South Dark World', ['Stumpy', 'Digging Game'], ['Dark Lake Hylia Drop (South)', 'Hype Cave', 'Swamp Palace', 'Village of Outcasts Heavy Rock', 'East Dark World Bridge', 'Inverted Links House', 'Archery Game', 'Bonk Fairy (Dark)',
@ -179,7 +183,7 @@ def create_inverted_regions(world, player):
'Hype Cave - Bottom', 'Hype Cave - Generous Guy']),
create_dw_region(player, 'West Dark World', ['Frog'], ['Village of Outcasts Drop', 'East Dark World River Pier', 'Brewery', 'C-Shaped House', 'Chest Game', 'Thieves Town', 'Bumper Cave Entrance Rock',
'Skull Woods Forest', 'Village of Outcasts Pegs', 'Village of Outcasts Eastern Rocks', 'Red Shield Shop', 'Inverted Dark Sanctuary', 'Fortune Teller (Dark)', 'Dark World Lumberjack Shop',
'West Dark World Teleporter', 'WDW Flute']),
'West Dark World Teleporter', 'WDW Flute', 'Dark World DMA Clip Spot', 'Bumper Cave Ledge Clip Spot']),
create_dw_region(player, 'Dark Grassy Lawn', None, ['Grassy Lawn Pegs', 'Dark World Shop', 'Dark Grassy Lawn Flute']),
create_dw_region(player, 'Hammer Peg Area', ['Dark Blacksmith Ruins'], ['Dark World Hammer Peg Cave', 'Peg Area Rocks', 'Hammer Peg Area Flute']),
create_dw_region(player, 'Bumper Cave Entrance', None, ['Bumper Cave (Bottom)', 'Bumper Cave Entrance Drop']),
@ -204,7 +208,9 @@ def create_inverted_regions(world, player):
create_cave_region(player, 'Mire Shed', 'a cave with two chests', ['Mire Shed - Left', 'Mire Shed - Right']),
create_cave_region(player, 'Dark Desert Hint', 'a storyteller'),
create_dw_region(player, 'Dark Death Mountain', None, ['Dark Death Mountain Drop (East)', 'Inverted Agahnims Tower', 'Superbunny Cave (Top)', 'Hookshot Cave', 'Turtle Rock',
'Spike Cave', 'Dark Death Mountain Fairy', 'Dark Death Mountain Teleporter (West)', 'Turtle Rock Tail Drop', 'DDM Flute']),
'Spike Cave', 'Dark Death Mountain Fairy', 'Dark Death Mountain Teleporter (West)', 'Turtle Rock Tail Drop', 'DDM Flute',
'Dark Death Mountain Ledge Clip Spot', 'Catfish Descent', 'Turtle Rock (Top) Clip Spot',
'Dark Death Mountain Descent']),
create_dw_region(player, 'Dark Death Mountain Ledge', None, ['Dark Death Mountain Ledge (East)', 'Dark Death Mountain Ledge (West)']),
create_dw_region(player, 'Turtle Rock (Top)', None, ['Dark Death Mountain Teleporter (East)', 'Turtle Rock Drop']),
create_dw_region(player, 'Dark Death Mountain Isolated Ledge', None, ['Turtle Rock Isolated Ledge Entrance']),
@ -297,7 +303,8 @@ def create_inverted_regions(world, player):
create_dungeon_region(player, 'Agahnim 2', 'Ganon\'s Tower', ['Ganons Tower - Validation Chest', 'Agahnim 2'], None),
create_cave_region(player, 'Pyramid', 'a drop\'s exit', ['Ganon'], ['Ganon Drop']),
create_cave_region(player, 'Bottom of Pyramid', 'a drop\'s exit', None, ['Pyramid Exit']),
create_dw_region(player, 'Pyramid Ledge', None, ['Pyramid Drop']), # houlihan room exits here in inverted
create_dw_region(player, 'Pyramid Ledge', None, ['Pyramid Drop']), # houlihan room exits here in inverted
create_dw_region(player, 'Death Mountain Bunny Descent Area', None, ['Light World Bunny Descent', 'East Death Mountain (Bottom) Jump']),
# to simplify flute connections
create_cave_region(player, 'The Sky', 'A Dark Sky', None, ['DDM Landing','NEDW Landing', 'WDW Landing', 'SDW Landing', 'EDW Landing', 'DD Landing', 'DLHL Landing'])

View File

@ -191,7 +191,7 @@ def generate_itempool(world, player):
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon,
lamps_needed_for_dark_rooms) = get_pool_core(world.progressive[player], world.shuffle[player],
world.difficulty[player], world.timer[player], world.goal[player],
world.mode[player], world.swords[player], world.retro[player])
world.mode[player], world.swords[player], world.retro[player], world.logic[player])
for item in precollected_items:
world.push_precollected(ItemFactory(item, player))
@ -391,7 +391,7 @@ def set_up_shops(world, player):
rss.locked = True
def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, retro):
def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, logic):
pool = []
placed_items = {}
precollected_items = []
@ -408,6 +408,12 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, r
def want_progressives():
return random.choice([True, False]) if progressive == 'random' else progressive == 'on'
# provide boots to major glitch dependent seeds
if logic in ['owglitches', 'nologic']:
precollected_items.append('Pegasus Boots')
pool.remove('Pegasus Boots')
pool.extend(['Rupees (20)'])
if want_progressives():
pool.extend(progressivegloves)
else:
@ -677,20 +683,21 @@ def test():
for progressive in ['on', 'off']:
for shuffle in ['full', 'insanity_legacy']:
for retro in [True, False]:
out = get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords,
retro)
count = len(out[0]) + len(out[1])
for logic in ['noglitches', 'owglitches']:
out = get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords,
retro, logic)
count = len(out[0]) + len(out[1])
correct_count = total_items_to_place
if goal == 'pedestal' and swords != 'vanilla':
# pedestal goals generate one extra item
correct_count += 1
if retro:
correct_count += 28
try:
assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, swords, retro))
except AssertionError as e:
print(e)
correct_count = total_items_to_place
if goal == 'pedestal' and swords != 'vanilla':
# pedestal goals generate one extra item
correct_count += 1
if retro:
correct_count += 28
try:
assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, swords, retro))
except AssertionError as e:
print(e)
if __name__ == '__main__':
test()

View File

@ -140,9 +140,8 @@ def main():
def get_weights(path):
try:
if urllib.parse.urlparse(path).scheme:
yaml = str(urllib.request.urlopen(path).read(), "utf-8")
else:
parsed_url = urllib.parse.urlparse(path)
if all(parsed_url.scheme, parsed_url.netloc, parsed_url.path):
with open(path, 'rb') as f:
yaml = str(f.read(), "utf-8")
except Exception as e:
@ -181,10 +180,10 @@ def roll_settings(weights):
if ret.name:
ret.name = handle_name(ret.name)
glitches_required = get_choice('glitches_required', weights)
if glitches_required not in ['none', 'no_logic']:
logging.warning("Only NMG and No Logic supported")
if glitches_required not in ['none', 'no_logic', 'overworld_glitches']:
logging.warning("Only NMG, OWG and No Logic supported")
glitches_required = 'none'
ret.logic = {None: 'noglitches', 'none': 'noglitches', 'no_logic': 'nologic'}[glitches_required]
ret.logic = {None: 'noglitches', 'none': 'noglitches', 'no_logic': 'nologic', 'overworld_glitches': 'owglitches'}[glitches_required]
# item_placement = get_choice('item_placement')
# not supported in ER

182
OWGSets.py Normal file
View File

@ -0,0 +1,182 @@
'''
Helper functions to deliver entrance/exit/region sets to OWG rules.
'''
def get_immediately_accessible_entrances(world, player):
'''
Entrances that are available with no items at all.
At this point, these are fake flipper spots.
'''
entrances = [
'Hobo Bridge',
'Zoras River',
'Lake Hylia Central Island Pier',
]
return entrances
def get_sword_required_superbunny_mirror_regions():
'''
Cave regions that superbunny can get through - but only with a sword.
'''
return [
'Mini Moldorm Cave',
'Spiral Cave (Top)',
]
def get_invalid_mirror_bunny_entrances_dw():
'''
Dark World entrances that can't be superbunny-mirrored into.
'''
return [
'Skull Woods Final Section (Entrance)',
'Hype Cave',
'Bonk Fairy (Dark)',
'Thieves Town',
'Dark World Hammer Peg Cave',
'Brewery',
'Hookshot Cave',
'Hookshot Cave Exit (South)',
'Dark Lake Hylia Ledge Fairy',
'Dark Lake Hylia Ledge Spike Cave',
]
def get_invalid_mirror_bunny_entrances_lw():
'''
Light World entrances that can't be superbunny-mirrored into.
A couple of these, like Blind's Hideout, are odd cases where the pixel
leading into the entrance prevents mirror superbunnying - generally due to
there being stairs there.
'''
return [
'Bonk Rock Cave',
'Bonk Fairy (Light)',
'Blinds Hideout',
'50 Rupee Cave',
'20 Rupee Cave',
'Checkerboard Cave',
'Light Hype Fairy',
'Waterfall of Wishing',
'Light World Bomb Hut',
'Mini Moldorm Cave',
'Ice Rod Cave',
'Hyrule Castle Secret Entrance Stairs',
'Sanctuary Grave',
'Kings Grave',
'Tower of Hera',
]
def get_superbunny_accessible_locations():
'''
Interior locations that can be accessed with superbunny state.
'''
return [
'Waterfall of Wishing - Left',
'Waterfall of Wishing - Right',
'King\'s Tomb', 'Floodgate',
'Floodgate Chest',
'Cave 45',
'Bonk Rock Cave',
'Brewery',
'C-Shaped House',
'Chest Game',
'Mire Shed - Left',
'Mire Shed - Right',
'Secret Passage',
'Ice Rod Cave',
'Pyramid Fairy - Left',
'Pyramid Fairy - Right',
'Superbunny Cave - Top',
'Superbunny Cave - Bottom',
]
def get_boots_clip_exits_lw(inverted = False):
'''
Special Light World region exits that require boots clips.
'''
exits = [
'Bat Cave River Clip Spot',
'Light World DMA Clip Spot',
'Hera Ascent',
'Death Mountain Return Ledge Clip Spot',
'Death Mountain Glitched Bridge',
'Zora Descent Clip Spot',
'Desert Northern Cliffs',
'Lake Hylia Island Clip Spot',
'Death Mountain Descent',
'Graveyard Ledge Clip Spot',
# Also requires a waterwalk setup, but the point still remains.
'Waterfall of Wishing',
]
if not inverted:
exits.append('Spectacle Rock Clip Spot')
exits.append('Bombos Tablet Clip Spot')
exits.append('Floating Island Clip Spot')
exits.append('Cave 45 Clip Spot')
return exits
def get_boots_clip_exits_dw(inverted = False):
'''
Special Dark World region exits that require boots clips.
'''
exits = [
'Dark World DMA Clip Spot',
'Bumper Cave Ledge Clip Spot',
'Catfish Descent',
'Hammer Pegs River Clip Spot',
'Dark Lake Hylia Ledge Clip Spot',
'Dark Desert Cliffs Clip Spot',
'Dark Death Mountain Descent',
]
if not inverted:
exits.append('Ganons Tower Ascent')
exits.append('Dark Death Mountain Glitched Bridge')
exits.append('Turtle Rock (Top) Clip Spot')
return exits
def get_glitched_speed_drops_dw():
'''
Dark World drop-down ledges that require glitched speed.
'''
return [
'Dark Death Mountain Ledge Clip Spot',
]
def get_mirror_clip_spots_dw():
'''
Mirror shenanigans that are in logic even if the player is a bunny.
'''
return [
'Dark Death Mountain Offset Mirror',
'Dark Death Mountain Bunny Descent Mirror Spot',
]
def get_mirror_clip_spots_lw():
'''
Inverted mirror shenanigans in logic even if the player is a bunny.
'''
return [
'Death Mountain Bunny Descent Mirror Spot',
'Death Mountain Offset Mirror',
]
def get_invalid_bunny_revival_dungeons():
'''
Dungeon regions that can't be bunny revived from.
'''
return [
'Tower of Hera (Bottom)',
'Swamp Palace (Entrance)',
'Turtle Rock (Entrance)',
]

View File

@ -14,7 +14,8 @@ def create_regions(world, player):
'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)',
'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', 'Light Hype 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',
'Light World DMA Clip Spot', 'Death Mountain Return Ledge Clip Spot', 'Desert Northern Cliffs', 'Lake Hylia Island Clip Spot', 'Bat Cave River Clip Spot', 'Bombos Tablet Clip Spot', 'Cave 45 Clip Spot']),
create_lw_region(player, 'Death Mountain Entrance', None, ['Old Man Cave (West)', 'Death Mountain Entrance Drop']),
create_lw_region(player, 'Lake Hylia Central Island', None, ['Capacity Upgrade', 'Lake Hylia Central Island Teleporter']),
create_cave_region(player, 'Blinds Hideout', 'a bounty of five items', ["Blind\'s Hideout - Top",
@ -117,7 +118,7 @@ def create_regions(world, player):
create_cave_region(player, 'Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)', 'Old Man Cave Exit (West)']),
create_cave_region(player, 'Old Man House', 'a connector', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']),
create_cave_region(player, 'Old Man House Back', 'a connector', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']),
create_lw_region(player, '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(player, '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', 'Death Mountain Glitched Bridge', 'Graveyard Ledge Clip Spot', 'Hera Ascent', 'Death Mountain Descent']),
create_cave_region(player, 'Death Mountain Return Cave', 'a connector', None, ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave Exit (East)']),
create_lw_region(player, 'Death Mountain Return Ledge', None, ['Death Mountain Return Ledge Drop', 'Death Mountain Return Cave (West)']),
create_cave_region(player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'], ['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']),
@ -136,7 +137,7 @@ def create_regions(world, player):
['Paradox Cave Push Block', 'Paradox Cave Bomb Jump']),
create_cave_region(player, 'Paradox Cave', 'a connector', None, ['Paradox Cave Exit (Middle)', 'Paradox Cave Exit (Top)', 'Paradox Cave Drop']),
create_cave_region(player, 'Light World Death Mountain Shop', 'a common shop'),
create_lw_region(player, '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(player, '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', 'Floating Island Clip Spot', 'Zora Descent Clip Spot']),
create_lw_region(player, 'Spiral Cave Ledge', None, ['Spiral Cave', 'Spiral Cave Ledge Drop']),
create_cave_region(player, 'Spiral Cave (Top)', 'a connector', ['Spiral Cave'], ['Spiral Cave (top to bottom)', 'Spiral Cave Exit (Top)']),
create_cave_region(player, 'Spiral Cave (Bottom)', 'a connector', None, ['Spiral Cave Exit']),
@ -145,19 +146,23 @@ def create_regions(world, player):
create_cave_region(player, 'Fairy Ascension Cave (Drop)', 'a connector', None, ['Fairy Ascension Cave Pots']),
create_cave_region(player, 'Fairy Ascension Cave (Top)', 'a connector', None, ['Fairy Ascension Cave Exit (Top)', 'Fairy Ascension Cave Drop']),
create_lw_region(player, 'Fairy Ascension Ledge', None, ['Fairy Ascension Ledge Drop', 'Fairy Ascension Cave (Top)']),
create_lw_region(player, 'Death Mountain (Top)', ['Ether Tablet'], ['East Death Mountain (Top)', 'Tower of Hera', 'Death Mountain Drop']),
create_lw_region(player, 'Death Mountain (Top)', ['Ether Tablet'], ['East Death Mountain (Top)', 'Tower of Hera', 'Death Mountain Drop', 'Spectacle Rock Clip Spot']),
create_lw_region(player, 'Spectacle Rock', ['Spectacle Rock'], ['Spectacle Rock Drop']),
create_dungeon_region(player, 'Tower of Hera (Bottom)', 'Tower of Hera', ['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(player, 'Tower of Hera (Basement)', 'Tower of Hera', ['Tower of Hera - Big Key Chest']),
create_dungeon_region(player, 'Tower of Hera (Top)', 'Tower of Hera', ['Tower of Hera - Compass Chest', 'Tower of Hera - Big Chest', 'Tower of Hera - Boss', 'Tower of Hera - Prize']),
create_dw_region(player, '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', 'Pyramid Hole', 'Northeast Dark World Broken Bridge Pass']),
create_dw_region(player, 'Northeast Dark World', ['Catfish'], ['West Dark World Gap', 'Dark World Potion Shop', 'East Dark World Broken Bridge Pass']),
'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',
'Hammer Pegs River Clip Spot', 'Dark Lake Hylia Ledge Clip Spot']),
create_dw_region(player, 'Catfish', ['Catfish'], ['Catfish Exit Rock']),
create_dw_region(player, 'Northeast Dark World', None, ['West Dark World Gap', 'Dark World Potion Shop', 'East Dark World Broken Bridge Pass', 'Catfish Entrance Rock']),
create_cave_region(player, 'Palace of Darkness Hint', 'a storyteller'),
create_cave_region(player, 'East Dark World Hint', 'a storyteller'),
create_dw_region(player, 'South Dark World', ['Stumpy', 'Digging Game', 'Bombos Tablet'], ['Dark Lake Hylia Drop (South)', 'Hype Cave', 'Swamp Palace', 'Village of Outcasts Heavy Rock', '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(player, 'South Dark World', ['Stumpy', 'Digging Game'], ['Dark Lake Hylia Drop (South)', 'Hype Cave', 'Swamp Palace', 'Village of Outcasts Heavy Rock', 'Maze Race Mirror Spot',
'Cave 45 Mirror Spot', 'East Dark World Bridge', 'Big Bomb Shop', 'Archery Game', 'Bonk Fairy (Dark)', 'Dark Lake Hylia Shop',
'Bombos Tablet Mirror Spot', 'Dark Desert Cliffs Clip Spot']),
create_lw_region(player, 'Bombos Tablet Ledge', ['Bombos Tablet']),
create_cave_region(player, 'Big Bomb Shop', 'the bomb shop'),
create_cave_region(player, 'Archery Game', 'a game of skill'),
create_dw_region(player, 'Dark Lake Hylia', None, ['Lake Hylia Island Mirror Spot', 'East Dark World Pier', 'Dark Lake Hylia Ledge']),
@ -168,7 +173,8 @@ def create_regions(world, player):
create_cave_region(player, 'Hype Cave', 'a bounty of five items', ['Hype Cave - Top', 'Hype Cave - Middle Right', 'Hype Cave - Middle Left',
'Hype Cave - Bottom', 'Hype Cave - Generous Guy']),
create_dw_region(player, 'West Dark World', ['Frog'], ['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',
'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']),
'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',
'Dark World DMA Clip Spot', 'Bumper Cave Ledge Clip Spot']),
create_dw_region(player, 'Dark Grassy Lawn', None, ['Grassy Lawn Pegs', 'Dark World Shop']),
create_dw_region(player, 'Hammer Peg Area', ['Dark Blacksmith Ruins'], ['Bat Cave Drop Ledge Mirror Spot', 'Dark World Hammer Peg Cave', 'Peg Area Rocks']),
create_dw_region(player, 'Bumper Cave Entrance', None, ['Bumper Cave (Bottom)', 'Bumper Cave Entrance Mirror Spot', 'Bumper Cave Entrance Drop']),
@ -193,9 +199,9 @@ def create_regions(world, player):
'Desert Palace Entrance (North) Mirror Spot', 'Dark Desert Hint', 'Dark Desert Fairy']),
create_cave_region(player, 'Mire Shed', 'a cave with two chests', ['Mire Shed - Left', 'Mire Shed - Right']),
create_cave_region(player, 'Dark Desert Hint', 'a storyteller'),
create_dw_region(player, 'Dark Death Mountain (West Bottom)', None, ['Spike Cave', 'Spectacle Rock Mirror Spot', 'Dark Death Mountain Fairy']),
create_dw_region(player, 'Dark Death Mountain (West Bottom)', None, ['Spike Cave', 'Spectacle Rock Mirror Spot', 'Dark Death Mountain Fairy', 'Ganons Tower Ascent', 'Dark Death Mountain Glitched Bridge', 'Dark Death Mountain Bunny Descent Mirror Spot', 'Dark Death Mountain Offset Mirror', 'Dark Death Mountain Descent']),
create_dw_region(player, 'Dark Death Mountain (Top)', None, ['Dark Death Mountain Drop (East)', 'Dark Death Mountain Drop (West)', 'Ganons Tower', 'Superbunny Cave (Top)',
'Hookshot Cave', 'East Death Mountain (Top) Mirror Spot', 'Turtle Rock']),
'Hookshot Cave', 'East Death Mountain (Top) Mirror Spot', 'Turtle Rock', 'Catfish Descent', 'Dark Death Mountain Ledge Clip Spot', 'Turtle Rock (Top) Clip Spot']),
create_dw_region(player, '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(player, 'Dark Death Mountain Isolated Ledge', None, ['Isolated Ledge Mirror Spot', 'Turtle Rock Isolated Ledge Entrance']),
create_dw_region(player, 'Dark Death Mountain (East Bottom)', None, ['Superbunny Cave (Bottom)', 'Cave Shop (Dark Death Mountain)', 'Fairy Ascension Mirror Spot']),
@ -290,7 +296,8 @@ def create_regions(world, player):
create_dungeon_region(player, 'Agahnim 2', 'Ganon\'s Tower', ['Ganons Tower - Validation Chest', 'Agahnim 2'], None),
create_cave_region(player, 'Pyramid', 'a drop\'s exit', ['Ganon'], ['Ganon Drop']),
create_cave_region(player, 'Bottom of Pyramid', 'a drop\'s exit', None, ['Pyramid Exit']),
create_dw_region(player, 'Pyramid Ledge', None, ['Pyramid Entrance', 'Pyramid Drop'])
create_dw_region(player, 'Pyramid Ledge', None, ['Pyramid Entrance', 'Pyramid Drop']),
create_dw_region(player, 'Dark Death Mountain Bunny Descent Area', None, ['West Dark World Bunny Descent', 'Dark Death Mountain (East Bottom) Jump']),
]
world.initialize_regions()

1
Rom.py
View File

@ -14,6 +14,7 @@ from Dungeons import dungeon_music_addresses
from Regions import location_table
from Text import MultiByteTextMapper, CompressedTextMapper, text_addresses, Credits, TextTable
from Text import Uncle_texts, Ganon1_texts, TavernMan_texts, Sahasrahla2_texts, Triforce_texts, Blind_texts, BombShop2_texts, junk_texts
from Text import KingsReturn_texts, Sanctuary_texts, Kakariko_texts, Blacksmiths_texts, DeathMountain_texts, LostWoods_texts, WishingWell_texts, DesertPalace_texts, MountainTower_texts, LinksHouse_texts, Lumberjacks_texts, SickKid_texts, FluteBoy_texts, Zora_texts, MagicShop_texts, Sahasrahla_names
from Utils import output_path, local_path, int16_as_bytes, int32_as_bytes, snes_to_pc
from Items import ItemFactory

239
Rules.py
View File

@ -1,6 +1,7 @@
import collections
import logging
from BaseClasses import CollectionState
import OWGSets
from BaseClasses import CollectionState, RegionType
def set_rules(world, player):
@ -38,6 +39,12 @@ def set_rules(world, player):
if world.logic[player] == 'noglitches':
no_glitches_rules(world, player)
elif world.logic[player] == 'owglitches':
logging.getLogger('').info('There is a chance OWG has bugged edge case rulesets, especially in inverted. Definitely file a report on GitHub if you see anything strange.')
# Initially setting no_glitches_rules to set the baseline rules for some
# entrances. The overworld_glitches_rules set is primarily additive.
no_glitches_rules(world, player)
overworld_glitches_rules(world, player)
elif world.logic[player] == 'minorglitches':
logging.getLogger('').info('Minor Glitches may be buggy still. No guarantee for proper logic checks.')
else:
@ -293,8 +300,7 @@ def global_rules(world, player):
set_rule(world.get_location('Ganons Tower - Bob\'s Torch', player), lambda state: state.has_Boots(player))
set_rule(world.get_entrance('Ganons Tower (Tile Room)', player), lambda state: state.has('Cane of Somaria', player))
set_rule(world.get_entrance('Ganons Tower (Hookshot Room)', player), lambda state: state.has('Hammer', player))
set_rule(world.get_entrance('Ganons Tower (Hookshot Room)', player), lambda state: state.has('Hammer', player) and (state.has('Hookshot', player) or state.has_Boots(player)))
set_rule(world.get_entrance('Ganons Tower (Map Room)', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 4) or (item_name(state, 'Ganons Tower - Map Chest', player) in [('Big Key (Ganons Tower)', player), ('Small Key (Ganons Tower)', player)] and state.has_key('Small Key (Ganons Tower)', player, 3)))
if world.accessibility[player] != 'locations':
set_always_allow(world.get_location('Ganons Tower - Map Chest', player), lambda state, item: item.name == 'Small Key (Ganons Tower)' and item.player == player and state.has_key('Small Key (Ganons Tower)', player, 3))
@ -356,7 +362,6 @@ def default_rules(world, player):
set_rule(world.get_entrance('Kings Grave Mirror Spot', player), lambda state: state.has_Pearl(player) and state.has_Mirror(player))
# 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)', player), lambda state: state.has_Boots(player))
set_rule(world.get_entrance('Bat Cave Drop Ledge', player), lambda state: state.has('Hammer', player))
set_rule(world.get_entrance('Lumberjack Tree Tree', player), lambda state: state.has_Boots(player) and state.has('Beat Agahnim 1', player))
set_rule(world.get_entrance('Bonk Rock Cave', player), lambda state: state.has_Boots(player))
set_rule(world.get_entrance('Desert Palace Stairs', player), lambda state: state.has('Book of Mudora', player))
@ -392,7 +397,8 @@ def default_rules(world, player):
set_rule(world.get_entrance('Turtle Rock Teleporter', player), lambda state: state.can_lift_heavy_rocks(player) and state.has('Hammer', player))
set_rule(world.get_entrance('East Death Mountain (Top)', player), lambda state: state.has('Hammer', player))
set_rule(world.get_location('Catfish', player), lambda state: state.can_lift_rocks(player))
set_rule(world.get_entrance('Catfish Exit Rock', player), lambda state: state.can_lift_rocks(player))
set_rule(world.get_entrance('Catfish Entrance Rock', player), lambda state: state.can_lift_rocks(player))
set_rule(world.get_entrance('Northeast Dark World Broken Bridge Pass', player), lambda state: state.has_Pearl(player) and (state.can_lift_rocks(player) or state.has('Hammer', player) or state.has('Flippers', player)))
set_rule(world.get_entrance('East Dark World Broken Bridge Pass', player), lambda state: state.has_Pearl(player) and (state.can_lift_rocks(player) or state.has('Hammer', player)))
set_rule(world.get_entrance('South Dark World Bridge', player), lambda state: state.has('Hammer', player) and state.has_Pearl(player))
@ -402,7 +408,7 @@ def default_rules(world, player):
set_rule(world.get_entrance('Hyrule Castle Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Hyrule Castle Main Gate', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: (state.has_Pearl(player) and state.has('Flippers', player) or state.has_Mirror(player))) # Overworld Bunny Revival
set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has_beam_sword(player) and state.has_Mirror(player))
set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has_beam_sword(player))
set_rule(world.get_entrance('Dark Lake Hylia Drop (South)', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) # ToDo any fake flipper set up?
set_rule(world.get_entrance('Dark Lake Hylia Ledge Fairy', player), lambda state: state.has_Pearl(player)) # bomb required
set_rule(world.get_entrance('Dark Lake Hylia Ledge Spike Cave', player), lambda state: state.can_lift_rocks(player) and state.has_Pearl(player))
@ -415,6 +421,7 @@ def default_rules(world, player):
set_rule(world.get_entrance('Skull Woods Second Section Hole', player), lambda state: state.has_Pearl(player)) # bunny cannot lift bush
set_rule(world.get_entrance('Maze Race Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Cave 45 Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Bombos Tablet Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('East Dark World Bridge', player), lambda state: state.has_Pearl(player) and state.has('Hammer', player))
set_rule(world.get_entrance('Lake Hylia Island Mirror Spot', player), lambda state: state.has_Pearl(player) and state.has_Mirror(player) and state.has('Flippers', player))
set_rule(world.get_entrance('Lake Hylia Central Island Mirror Spot', player), lambda state: state.has_Mirror(player))
@ -460,6 +467,19 @@ def default_rules(world, player):
set_rule(world.get_entrance('Ganons Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player))
def forbid_overworld_glitches(world, player):
for exit in OWGSets.get_boots_clip_exits_lw(world.mode[player] == 'inverted'):
set_rule(world.get_entrance(exit, player), lambda state: False)
for exit in OWGSets.get_boots_clip_exits_dw(world.mode[player] == 'inverted'):
set_rule(world.get_entrance(exit, player), lambda state: False)
for exit in OWGSets.get_glitched_speed_drops_dw():
set_rule(world.get_entrance(exit, player), lambda state: False)
if world.mode[player] != 'inverted':
for exit in OWGSets.get_mirror_clip_spots_dw():
set_rule(world.get_entrance(exit, player), lambda state: False)
def inverted_rules(world, player):
# s&q regions. link's house entrance is set to true so the filler knows the chest inside can always be reached
world.get_region('Inverted Links House', player).can_reach_private = lambda state: True
@ -631,6 +651,7 @@ def no_glitches_rules(world, player):
set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player) and (state.has('Hammer', player) or state.can_lift_rocks(player)))
set_rule(world.get_entrance('Dark Lake Hylia Ledge Drop', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player))
else:
set_rule(world.get_entrance('Bat Cave Drop Ledge', player), lambda state: state.has('Hammer', player))
set_rule(world.get_entrance('Zoras River', player), lambda state: state.has_Pearl(player) and (state.has('Flippers', player) or state.can_lift_rocks(player)))
set_rule(world.get_entrance('Lake Hylia Central Island Pier', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) # can be fake flippered to
set_rule(world.get_entrance('Lake Hylia Island Pier', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) # can be fake flippered to
@ -642,25 +663,48 @@ def no_glitches_rules(world, player):
set_rule(world.get_entrance('Dark Lake Hylia Ledge Drop', player), lambda state: state.has('Flippers', player))
set_rule(world.get_entrance('East Dark World Pier', player), lambda state: state.has('Flippers', player))
add_rule(world.get_entrance('Ganons Tower (Hookshot Room)', player), lambda state: state.has('Hookshot', player) or state.has_Boots(player))
add_rule(world.get_entrance('Ganons Tower (Double Switch Room)', player), lambda state: state.has('Hookshot', player))
set_rule(world.get_entrance('Paradox Cave Push Block Reverse', player), lambda state: False) # no glitches does not require block override
forbid_bomb_jump_requirements(world, player)
forbid_overworld_glitches(world, player)
add_conditional_lamps(world, player)
def forbid_bomb_jump_requirements(world, player):
DMs_room_chests = ['Ganons Tower - DMs Room - Top Left', 'Ganons Tower - DMs Room - Top Right', 'Ganons Tower - DMs Room - Bottom Left', 'Ganons Tower - DMs Room - Bottom Right']
for location in DMs_room_chests:
add_rule(world.get_location(location, player), lambda state: state.has('Hookshot', player))
set_rule(world.get_entrance('Paradox Cave Push Block Reverse', player), lambda state: False) # no glitches does not require block override
set_rule(world.get_entrance('Paradox Cave Bomb Jump', player), lambda state: False)
set_rule(world.get_entrance('Skull Woods First Section Bomb Jump', player), lambda state: False)
DW_Entrances = ['Bumper Cave (Bottom)',
'Superbunny Cave (Top)',
'Superbunny Cave (Bottom)',
'Hookshot Cave',
'Bumper Cave (Top)',
'Hookshot Cave Back Entrance',
'Dark Death Mountain Ledge (East)',
'Turtle Rock Isolated Ledge Entrance',
'Thieves Town',
'Skull Woods Final Section',
'Ice Palace',
'Misery Mire',
'Palace of Darkness',
'Swamp Palace',
'Turtle Rock',
'Dark Death Mountain Ledge (West)']
def check_is_dark_world(region):
for entrance in region.entrances:
if entrance.name in DW_Entrances:
return True
return False
def add_conditional_lamps(world, player):
# Light cones in standard depend on which world we actually are in, not which one the location would normally be
# We add Lamp requirements only to those locations which lie in the dark world (or everything if open
DW_Entrances = ['Bumper Cave (Bottom)', 'Superbunny Cave (Top)', 'Superbunny Cave (Bottom)', 'Hookshot Cave', 'Bumper Cave (Top)', 'Hookshot Cave Back Entrance', 'Dark Death Mountain Ledge (East)',
'Turtle Rock Isolated Ledge Entrance', 'Thieves Town', 'Skull Woods Final Section', 'Ice Palace', 'Misery Mire', 'Palace of Darkness', 'Swamp Palace', 'Turtle Rock', 'Dark Death Mountain Ledge (West)']
def check_is_dark_world(region):
for entrance in region.entrances:
if entrance.name in DW_Entrances:
return True
return False
def add_conditional_lamp(spot, region, spottype='Location'):
if spottype == 'Location':
@ -699,6 +743,54 @@ def no_glitches_rules(world, player):
add_lamp_requirement(world.get_entrance('Throne Room', player), player)
def overworld_glitches_rules(world, player):
# Spots that are immediately accessible.
for entrance in OWGSets.get_immediately_accessible_entrances(world, player):
set_rule(world.get_entrance(entrance, player), lambda state: True)
# Boots-accessible locations.
for entrance in OWGSets.get_boots_clip_exits_lw(world.mode[player] == 'inverted'):
set_rule(world.get_entrance(entrance, player), lambda state: state.can_boots_clip_lw(player))
for entrance in OWGSets.get_boots_clip_exits_dw(world.mode[player] == 'inverted'):
set_rule(world.get_entrance(entrance, player), lambda state: state.can_boots_clip_dw(player))
# Glitched speed drops.
for drop in OWGSets.get_glitched_speed_drops_dw():
set_rule(world.get_entrance(drop, player), lambda state: state.can_get_glitched_speed_dw(player))
# Dark Death Mountain Ledge Clip Spot also accessible with mirror.
if world.mode[player] != 'inverted':
add_rule(world.get_entrance('Dark Death Mountain Ledge Clip Spot', player), lambda state: state.has_Mirror(player), 'or')
# Mirror clip spots.
if world.mode[player] != 'inverted':
for clip_spot in OWGSets.get_mirror_clip_spots_dw():
set_rule(world.get_entrance(clip_spot, player), lambda state: state.has_Mirror(player))
else:
for clip_spot in OWGSets.get_mirror_clip_spots_lw():
set_rule(world.get_entrance(clip_spot, player), lambda state: state.has_Mirror(player))
# Locations that you can superbunny mirror into, but need a sword to clear.
mini_moldorm_cave = world.get_region('Mini Moldorm Cave', player)
for superbunny_mirror_weapon_region in OWGSets.get_sword_required_superbunny_mirror_regions():
region = world.get_region(superbunny_mirror_weapon_region, player)
if check_is_dark_world(region):
for spot in region.locations:
add_rule(world.get_location(spot, player), lambda state: state.can_superbunny_mirror_with_sword(player), 'or')
# Regions that require the boots and some other stuff.
if world.mode[player] != 'inverted':
set_rule(world.get_entrance('Dark Desert Teleporter', player), lambda state: state.has('Ocarina', player) or (state.can_boots_clip_dw(player) and state.can_lift_heavy_rocks(player)))
set_rule(world.get_entrance('Turtle Rock Teleporter', player), lambda state: (state.can_boots_clip_dw(player) or state.can_lift_heavy_rocks(player)) and state.has('Hammer', player))
add_rule(world.get_entrance('Catfish Exit Rock', player), lambda state: state.can_boots_clip_dw(player), 'or')
add_rule(world.get_entrance('East Dark World Broken Bridge Pass', player), lambda state: state.can_boots_clip_dw(player), 'or')
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('Dark Death Mountain (West Bottom)', 'Region', player) and state.has_Mirror(player))
else:
add_rule(world.get_entrance('South Dark World Teleporter', player), lambda state: state.has_Boots(player) and state.can_lift_rocks(player), 'or')
# Zora's Ledge via waterwalk setup.
add_rule(world.get_location('Zora\'s Ledge', player), lambda state: state.has_Boots(player), 'or')
def open_rules(world, player):
# softlock protection as you can reach the sewers small key door with a guard drop key
set_rule(world.get_location('Hyrule Castle - Boomerang Chest', player), lambda state: state.has_key('Small Key (Escape)', player))
@ -1260,7 +1352,6 @@ def set_inverted_big_bomb_rules(world, player):
def set_bunny_rules(world, player):
# 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.
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)',
@ -1275,9 +1366,20 @@ def set_bunny_rules(world, player):
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(player)
def get_rule_to_add(region, location = None, connecting_entrance = None):
# In OWG, a location can potentially be superbunny-mirror accessible or
# bunny revival accessible.
if world.logic[player] == 'owglitches':
if region.name in OWGSets.get_invalid_bunny_revival_dungeons():
return lambda state: state.has_Mirror(player) or state.has_Pearl(player)
if not any([
None not in [location, connecting_entrance] and location.name in OWGSets.get_superbunny_accessible_locations() and connecting_entrance.name not in OWGSets.get_invalid_mirror_bunny_entrances_dw(),
not region.is_light_world]):
return lambda state: state.has_Pearl(player)
else:
if not region.is_light_world:
return lambda state: state.has_Pearl(player)
# in this case we are mixed region.
# we collect possible options.
@ -1300,11 +1402,18 @@ def set_bunny_rules(world, player):
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
# For OWG, establish superbunny and revival rules.
if world.logic[player] == 'owglitches' and entrance.name not in OWGSets.get_invalid_mirror_bunny_entrances_dw():
for location in entrance.connected_region.locations:
if location.name in OWGSets.get_superbunny_accessible_locations():
possible_options.append(lambda state: path_to_access_rule(new_path, entrance) and state.has_Mirror(player))
continue
else:
continue
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
# we have reached pure light world or a dungeon, so we have a new possible option
possible_options.append(path_to_access_rule(new_path, entrance))
return options_to_access_rule(possible_options)
@ -1321,24 +1430,32 @@ def set_bunny_rules(world, player):
if paradox_shop.is_dark_world:
add_rule(paradox_shop.entrances[0], get_rule_to_add(paradox_shop))
# Add requirements for all locations that are actually in the dark world, except those available to the bunny
for location in world.get_locations():
if location.player == player and location.parent_region.is_dark_world:
if location.name in bunny_accessible_locations:
continue
add_rule(location, get_rule_to_add(location.parent_region))
# Add requirements for all locations that are actually in the dark world, except those available to the bunny, including dungeon revival
for entrance in world.get_entrances():
if entrance.player == player and entrance.parent_region.is_dark_world:
if world.logic[player] == 'owglitches':
if entrance.connected_region.type == RegionType.Dungeon:
if entrance.connected_region.name in OWGSets.get_invalid_bunny_revival_dungeons():
add_rule(entrance, get_rule_to_add(entrance.connected_region, None, entrance))
continue
if entrance.connected_region.name == 'Turtle Rock (Entrance)':
add_rule(world.get_entrance('Turtle Rock Entrance Gap', player), get_rule_to_add(entrance.connected_region, None, entrance))
if entrance.name in OWGSets.get_invalid_mirror_bunny_entrances_dw():
continue
for location in entrance.connected_region.locations:
if world.logic[player] == 'owglitches' and entrance.name in OWGSets.get_invalid_mirror_bunny_entrances_dw():
add_rule(location, get_rule_to_add(entrance.connected_region, location, entrance))
continue
if location.name in bunny_accessible_locations:
continue
add_rule(location, get_rule_to_add(entrance.connected_region, location))
def set_inverted_bunny_rules(world, player):
# 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.
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)', 'Desert Palace Main (Inner)', 'Fairy Ascension Cave (Drop)', 'The Sky']
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', 'Dark Blacksmith Ruins', 'Bombos Tablet', 'Ether Tablet', 'Purple Chest']
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', 'Dark Blacksmith Ruins', 'Bombos Tablet Ledge', 'Ether Tablet', 'Purple Chest']
def path_to_access_rule(path, entrance):
return lambda state: state.can_reach(entrance) and all(rule(state) for rule in path)
@ -1346,9 +1463,19 @@ def set_inverted_bunny_rules(world, player):
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_dark_world:
return lambda state: state.has_Pearl(player)
def get_rule_to_add(region, location = None, connecting_entrance = None):
# In OWG, a location can potentially be superbunny-mirror accessible or
# bunny revival accessible.
if world.logic[player] == 'owglitches':
if region.name in OWGSets.get_invalid_bunny_revival_dungeons():
return lambda state: state.has_Mirror(player) or state.has_Pearl(player)
if not any([
None not in [location, connecting_entrance] and location.name in OWGSets.get_superbunny_accessible_locations() and connecting_entrance.name not in OWGSets.get_invalid_mirror_bunny_entrances_lw(),
not region.is_dark_world]):
return lambda state: state.has_Pearl(player)
else:
if not region.is_dark_world:
return lambda state: state.has_Pearl(player)
# in this case we are mixed region.
# we collect possible options.
@ -1371,7 +1498,14 @@ def set_inverted_bunny_rules(world, player):
new_path = path + [entrance.access_rule]
seen.add(new_region)
if not new_region.is_dark_world:
continue # we don't care about pure light world entrances
# For OWG, establish superbunny and revival rules.
if world.logic[player] == 'owglitches' and entrance.name not in OWGSets.get_invalid_mirror_bunny_entrances_lw():
for location in entrance.connected_region.locations:
if location.name in OWGSets.get_superbunny_accessible_locations():
possible_options.append(lambda state: path_to_access_rule(new_path, entrance) and state.has_Mirror(player))
continue
else:
continue
if new_region.is_light_world:
queue.append((new_region, new_path))
else:
@ -1392,13 +1526,22 @@ def set_inverted_bunny_rules(world, player):
if paradox_shop.is_light_world:
add_rule(paradox_shop.entrances[0], get_rule_to_add(paradox_shop))
# Add requirements for all locations that are actually in the light world, except those available to the bunny
for location in world.get_locations():
if location.player == player and location.parent_region.is_light_world:
if location.name in bunny_accessible_locations:
continue
add_rule(location, get_rule_to_add(location.parent_region))
# Add requirements for all locations that are actually in the light world, except those available to the bunny, including dungeon revival
for entrance in world.get_entrances():
if entrance.player == player and entrance.parent_region.is_light_world:
if world.logic[player] == 'owglitches':
if entrance.connected_region.type == RegionType.Dungeon:
if entrance.connected_region.name in OWGSets.get_invalid_bunny_revival_dungeons():
add_rule(entrance, get_rule_to_add(entrance.connected_region, None, entrance))
continue
if entrance.connected_region.name == 'Turtle Rock (Entrance)':
add_rule(world.get_entrance('Turtle Rock Entrance Gap', player), get_rule_to_add(entrance.connected_region, None, entrance))
if entrance.name in OWGSets.get_invalid_mirror_bunny_entrances_lw():
continue
for location in entrance.connected_region.locations:
if world.logic[player] == 'owglitches' and entrance.name in OWGSets.get_invalid_mirror_bunny_entrances_lw():
add_rule(location, get_rule_to_add(entrance.connected_region, location, entrance))
continue
if location.name in bunny_accessible_locations:
continue
add_rule(location, get_rule_to_add(entrance.connected_region, location))