diff --git a/BaseClasses.py b/BaseClasses.py index c6d63713..d1e102f0 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -508,6 +508,28 @@ class CollectionState(object): def has_turtle_rock_medallion(self, player): 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_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, event=False, location=None): if location: self.locations_checked.add(location) diff --git a/EntranceRandomizer.py b/EntranceRandomizer.py index 46908a9b..215c098c 100755 --- a/EntranceRandomizer.py +++ b/EntranceRandomizer.py @@ -19,14 +19,16 @@ class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter): def start(): parser = argparse.ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter) parser.add_argument('--create_spoiler', help='Output a Spoiler File', action='store_true') - parser.add_argument('--logic', default='noglitches', const='noglitches', nargs='?', choices=['noglitches', 'minorglitches', 'nologic'], + parser.add_argument('--logic', default='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='open', const='open', nargs='?', choices=['standard', 'open', 'inverted'], help='''\ diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 08f8dfce..a53b7756 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -2911,6 +2911,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'), @@ -2953,6 +2955,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)'), @@ -3015,7 +3018,41 @@ 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'), + ('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'), + ('Hookshot Cave Island Clip Spot', 'Death Mountain Floating Island (Dark World)'), + ('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'), @@ -3075,6 +3112,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'), @@ -3229,7 +3268,33 @@ 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'), + ('Hookshot Cave Island Clip Spot', 'Death Mountain Floating Island (Dark World)'), + ('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"), diff --git a/Fill.py b/Fill.py index bfc1145e..527fb559 100644 --- a/Fill.py +++ b/Fill.py @@ -221,7 +221,7 @@ def distribute_items_restrictive(world, gftower_trash_count=0, fill_locations=No # fill in gtower locations with trash first for player in range(1, world.players + 1): - if world.ganonstower_vanilla[player]: + if world.ganonstower_vanilla[player] and world.logic[player] not in ['owglitches']: gtower_locations = [location for location in fill_locations if 'Ganons Tower' in location.name and location.player == player] random.shuffle(gtower_locations) trashcnt = 0 diff --git a/Gui.py b/Gui.py index e02457d1..f753d5a3 100755 --- a/Gui.py +++ b/Gui.py @@ -150,7 +150,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) diff --git a/InvertedRegions.py b/InvertedRegions.py index 7623b398..aeccb7a2 100644 --- a/InvertedRegions.py +++ b/InvertedRegions.py @@ -17,7 +17,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']), @@ -124,7 +125,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']), 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)']), @@ -145,7 +147,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)']), @@ -162,8 +164,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)', @@ -178,7 +182,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']), @@ -203,7 +207,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', 'Hookshot Cave Island 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']), @@ -296,7 +302,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']) diff --git a/ItemList.py b/ItemList.py index 7fb78342..b99f6941 100644 --- a/ItemList.py +++ b/ItemList.py @@ -180,7 +180,7 @@ def generate_itempool(world, player): (pool, placed_items, precollected_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.swords, world.retro, world.customitemarray) world.rupoor_cost = min(world.customitemarray[67], 9999) else: - (pool, placed_items, precollected_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.swords, world.retro) + (pool, placed_items, precollected_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.swords, world.retro, world.logic) world.itempool += ItemFactory(pool, player) for item in precollected_items: world.push_precollected(ItemFactory(item, player)) @@ -345,7 +345,7 @@ def set_up_shops(world, player): #special shop types -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 = [] @@ -358,6 +358,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 boots glitch dependent modes + 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: @@ -634,19 +640,20 @@ 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', 'nologic']: + 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() diff --git a/Regions.py b/Regions.py index 70ae3b0b..3050ad99 100644 --- a/Regions.py +++ b/Regions.py @@ -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,8 +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_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', 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)']), create_cave_region(player, 'Spectacle Rock Cave (Bottom)', 'a connector', None, ['Spectacle Rock Cave Exit']), @@ -136,8 +136,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, 'Spiral Cave Ledge', None, ['Spiral Cave', 'Spiral Cave Ledge Drop']), + 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']), create_lw_region(player, 'Fairy Ascension Plateau', None, ['Fairy Ascension Drop', 'Fairy Ascension Cave (Bottom)']), @@ -145,19 +144,22 @@ 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, 'Spectacle Rock', ['Spectacle Rock'], ['Spectacle Rock 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 +170,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 +196,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', 'Hookshot Cave Island Clip Spot', '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 +293,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']), ] for region_name, (room_id, shopkeeper, replaceable) in shop_table.items(): diff --git a/Rules.py b/Rules.py index 0138e09b..ea632740 100644 --- a/Rules.py +++ b/Rules.py @@ -1,6 +1,7 @@ import collections import logging -from BaseClasses import CollectionState +import OWGSets +from BaseClasses import CollectionState, RegionType def set_rules(world, player): @@ -37,6 +38,12 @@ def set_rules(world, player): no_glitches_rules(world, player) elif world.logic == 'minorglitches': logging.getLogger('').info('Minor Glitches may be buggy still. No guarantee for proper logic checks.') + elif world.logic == '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) else: raise NotImplementedError('Not implemented yet') @@ -183,7 +190,8 @@ def global_rules(world, player): set_rule(world.get_location('Ether Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has_beam_sword(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)) @@ -193,7 +201,7 @@ def global_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)) @@ -206,6 +214,7 @@ def global_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)) @@ -851,6 +860,17 @@ def inverted_rules(world, player): set_rule(world.get_entrance('Inverted Ganons Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt, player)) +def forbid_overworld_glitches(world, player): + for exit in OWGSets.get_boots_clip_exits_lw(world.mode == 'inverted'): + set_rule(world.get_entrance(exit, player), lambda state: False) + for exit in OWGSets.get_boots_clip_exits_dw(world.mode == '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 no_glitches_rules(world, player): if world.mode != 'inverted': set_rule(world.get_entrance('Zoras River', player), lambda state: state.has('Flippers', player) or state.can_lift_rocks(player)) @@ -860,6 +880,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('Hobo Bridge', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) @@ -873,20 +894,29 @@ def no_glitches_rules(world, player): 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 + 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 Bomb Jump', player), lambda state: False) set_rule(world.get_entrance('Skull Woods First Section Bomb Jump', player), lambda state: False) - # 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)'] +# 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 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): def add_conditional_lamp(spot, region, spottype='Location'): if spottype == 'Location': spot = world.get_location(spot, player) @@ -923,6 +953,54 @@ def no_glitches_rules(world, player): add_lamp_requirement(world.get_entrance('Sewers Back Door', player), player) add_lamp_requirement(world.get_entrance('Throne Room', player), player) +def overworld_glitches_rules(world, player): + # @TODO: Waterfall fairy and Zora ledge could use some logic to determine + # if we can water walk and/or stored water walk in; currently it's omitted + # in case no interiors provide a water walk, but one could be kicking + # around. We could detect a path to determine if one can be stored. + + # 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 == '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 == '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 != '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 != '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. + superbunny_mirror_weapon = lambda state: state.has_Mirror(player) and state.has_sword(player) + 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), superbunny_mirror_weapon, 'or') + + # Regions that require the boots and some other stuff. + if world.mode != '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') def open_rules(world, player): # softlock protection as you can reach the sewers small key door with a guard drop key @@ -1494,8 +1572,15 @@ 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: + def get_rule_to_add(region, location = None): + # In OWG, a location can potentially be superbunny-mirror accessible or + # bunny revival accessible. + if world.logic == 'owglitches' and not any([ + location in OWGSets.get_superbunny_accessible_locations() and region.name not in OWGSets.get_invalid_mirror_bunny_entrances_dw(), + region.type == RegionType.Dungeon and region.name != 'Swamp Palace (Entrance)', + not region.is_light_world]): + return lambda state: state.has_Pearl(player) + elif world.logic != 'owglitches' and not region.is_light_world: return lambda state: state.has_Pearl(player) # in this case we are mixed region. # we collect possible options. @@ -1519,7 +1604,14 @@ 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 == '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: @@ -1546,8 +1638,11 @@ def set_bunny_rules(world, player): if location.name in bunny_accessible_locations: continue + if world.logic == 'owglitches' and location.parent_region.name not in OWGSets.get_invalid_mirror_bunny_entrances_dw() and location.name in OWGSets.get_superbunny_accessible_locations(): + add_rule(location, get_rule_to_add(location.parent_region, location.name)) + continue - add_rule(location, get_rule_to_add(location.parent_region)) + add_rule(location, get_rule_to_add(location.parent_region, location.name)) def set_inverted_bunny_rules(world, player): @@ -1556,7 +1651,7 @@ def set_inverted_bunny_rules(world, player): 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): @@ -1565,8 +1660,15 @@ 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: + def get_rule_to_add(region, location = None): + # In OWG, a location can potentially be superbunny-mirror accessible or + # bunny revival accessible. + if world.logic == 'owglitches' and not any([ + location in OWGSets.get_superbunny_accessible_locations() and region.name not in OWGSets.get_invalid_mirror_bunny_entrances_dw(), + region.type == RegionType.Dungeon and region.name != 'Swamp Palace (Entrance)', + not region.is_dark_world]): + return lambda state: state.has_Pearl(player) + elif world.logic != 'owglitches' and not region.is_dark_world: return lambda state: state.has_Pearl(player) # in this case we are mixed region. # we collect possible options. @@ -1590,7 +1692,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 == '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: @@ -1617,7 +1726,10 @@ def set_inverted_bunny_rules(world, player): if location.name in bunny_accessible_locations: continue + if world.logic == 'owglitches' and location.parent_region.name not in OWGSets.get_invalid_mirror_bunny_entrances_lw() and location.name in OWGSets.get_superbunny_accessible_locations(): + add_rule(location, get_rule_to_add(location.parent_region, location.name)) + continue - add_rule(location, get_rule_to_add(location.parent_region)) + add_rule(location, get_rule_to_add(location.parent_region, location.name))