Merge pull request #110 from compiling/multiworld

OWG mandatory exit rules
This commit is contained in:
Fabian Dill 2020-06-13 19:36:24 +02:00 committed by GitHub
commit 35933b88c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 78 additions and 28 deletions

View File

@ -1910,9 +1910,7 @@ def connect_random(world, exitlist, targetlist, player, two_way=False):
def connect_mandatory_exits(world, entrances, caves, must_be_exits, player):
"""This works inplace"""
random.shuffle(entrances)
random.shuffle(caves)
# Keeps track of entrances that cannot be used to access each exit / cave
if world.mode[player] == 'inverted':
invalid_connections = Inverted_Must_Exit_Invalid_Connections.copy()
@ -1920,6 +1918,18 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits, player):
invalid_connections = Must_Exit_Invalid_Connections.copy()
invalid_cave_connections = defaultdict(set)
if world.logic[player] in ['owglitches', 'nologic']:
import OverworldGlitchRules
for entrance in OverworldGlitchRules.get_non_mandatory_exits(world.mode[player] == 'inverted'):
invalid_connections[entrance] = set()
if entrance in must_be_exits:
must_be_exits.remove(entrance)
entrances.append(entrance)
"""This works inplace"""
random.shuffle(entrances)
random.shuffle(caves)
# Handle inverted Aga Tower - if it depends on connections, then so does Hyrule Castle Ledge
if world.mode[player] == 'inverted':
for entrance in invalid_connections:

View File

@ -92,6 +92,37 @@ def get_superbunny_accessible_locations():
yield location
def get_non_mandatory_exits(inverted):
"""
Entrances that can be reached with full equipment using overworld glitches and don't need to be an exit.
The following are still be mandatory exits:
Open:
Turtle Rock Isolated Ledge Entrance
Skull Woods Second Section Door (West) (or Skull Woods Final Section)
Inverted:
Two Brothers House (West)
Desert Palace Entrance (East)
"""
yield 'Bumper Cave (Top)'
yield 'Death Mountain Return Cave (West)'
yield 'Hookshot Cave Back Entrance'
if inverted:
yield 'Desert Palace Entrance (North)'
yield 'Desert Palace Entrance (West)'
yield 'Inverted Ganons Tower'
yield 'Hyrule Castle Entrance (West)'
yield 'Hyrule Castle Entrance (East)'
else:
yield 'Dark Death Mountain Ledge (West)'
yield 'Dark Death Mountain Ledge (East)'
yield 'Mimic Cave'
yield 'Desert Palace Entrance (East)'
def get_boots_clip_exits_lw(inverted = False):
"""
Special Light World region exits that require boots clips.
@ -120,7 +151,7 @@ def get_boots_clip_exits_lw(inverted = False):
yield ('Cave 45 Clip Spot', 'Light World', 'Cave 45 Ledge')
def get_boots_clip_exits_dw(inverted = False):
def get_boots_clip_exits_dw(inverted, player):
"""
Special Dark World region exits that require boots clips.
"""
@ -139,6 +170,7 @@ def get_boots_clip_exits_dw(inverted = False):
yield ('Ganons Tower Ascent', 'Dark Death Mountain (West Bottom)', 'Dark Death Mountain (Top)') # This only gets you to the GT entrance
yield ('Dark Death Mountain Glitched Bridge', 'Dark Death Mountain (West Bottom)', 'Dark Death Mountain (Top)')
yield ('Turtle Rock (Top) Clip Spot', 'Dark Death Mountain (Top)', 'Turtle Rock (Top)')
yield ('Ice Palace Clip', 'South Dark World', 'Dark Lake Hylia Central Island', lambda state: state.can_boots_clip_dw(player) and state.has('Flippers', player))
else:
yield ('Dark Desert Teleporter Clip Spot', 'Dark Desert', 'Dark Desert Ledge')
@ -152,26 +184,33 @@ def get_glitched_speed_drops_dw(inverted = False):
def get_mirror_clip_spots_dw():
"""
Mirror shenanigans that are in logic even if the player is a bunny.
Out of bounds transitions using the mirror
"""
yield ('Dark Death Mountain Offset Mirror', 'Dark Death Mountain (West Bottom)', 'East Dark World')
yield ('Dark Death Mountain Bunny Descent Mirror Spot', 'Dark Death Mountain (West Bottom)', 'Dark Death Mountain Bunny Descent Area')
yield ('West Dark World Bunny Descent', 'Dark Death Mountain Bunny Descent Area', 'West Dark World')
yield ('Dark Death Mountain (East Bottom) Jump', 'Dark Death Mountain Bunny Descent Area', 'Dark Death Mountain (East Bottom)')
yield ('Desert East Mirror Clip', 'Dark Desert', 'Desert Palace Lone Stairs')
def get_mirror_clip_spots_lw():
def get_mirror_offset_spots_dw():
"""
Inverted mirror shenanigans in logic even if the player is a bunny.
Mirror shenanigans placing a mirror portal with a broken camera
"""
yield ('Death Mountain Bunny Descent Mirror Spot', 'Death Mountain', 'Death Mountain Bunny Descent Area')
yield ('Light World Bunny Descent', 'Death Mountain Bunny Descent Area', 'Light World')
yield ('East Death Mountain (Bottom) Jump', 'Death Mountain Bunny Descent Area', 'East Death Mountain (Bottom)')
yield ('Dark Death Mountain Offset Mirror', 'Dark Death Mountain (West Bottom)', 'East Dark World')
def get_mirror_offset_spots_lw(player):
"""
Mirror shenanigans placing a mirror portal with a broken camera
"""
yield ('Death Mountain Offset Mirror', 'Death Mountain', 'Light World')
yield ('Death Mountain Offset Mirror (Houlihan Exit)', 'Death Mountain', 'Hyrule Castle Ledge', lambda state: state.has_Mirror(player) and state.can_boots_clip_dw(player) and state.has_Pearl(player))
def get_invalid_bunny_revival_dungeons():
"""
Dungeon regions that can't be bunny revived from.
Dungeon regions that can't be bunny revived from without superbunny state.
"""
yield 'Tower of Hera (Bottom)'
yield 'Swamp Palace (Entrance)'
@ -183,10 +222,7 @@ def overworld_glitches_rules(world, player):
# Boots-accessible locations.
create_owg_connections(player, world, get_boots_clip_exits_lw(world.mode[player] == 'inverted'), lambda state: state.can_boots_clip_lw(player))
create_owg_connections(player, world, get_boots_clip_exits_dw(world.mode[player] == 'inverted'), lambda state: state.can_boots_clip_dw(player))
if world.mode[player] != 'inverted':
create_owg_connections(player, world, [('Ice Palace Clip', 'South Dark World', 'Dark Lake Hylia Central Island')], lambda state: state.can_boots_clip_dw(player) and state.has('Flippers', player))
create_owg_connections(player, world, get_boots_clip_exits_dw(world.mode[player] == 'inverted', player), lambda state: state.can_boots_clip_dw(player))
# Glitched speed drops.
create_owg_connections(player, world, get_glitched_speed_drops_dw(world.mode[player] == 'inverted'), lambda state: state.can_get_glitched_speed_dw(player))
@ -197,8 +233,9 @@ def overworld_glitches_rules(world, player):
# Mirror clip spots.
if world.mode[player] != 'inverted':
create_owg_connections(player, world, get_mirror_clip_spots_dw(), lambda state: state.has_Mirror(player))
create_owg_connections(player, world, get_mirror_offset_spots_dw(), lambda state: state.has_Mirror(player) and state.can_boots_clip_lw(player))
else:
create_owg_connections(player, world, get_mirror_clip_spots_lw(), lambda state: state.has_Mirror(player))
create_owg_connections(player, world, get_mirror_offset_spots_lw(player), lambda state: state.has_Mirror(player) and state.can_boots_clip_dw(player))
# Regions that require the boots and some other stuff.
if world.mode[player] != 'inverted':
@ -220,11 +257,12 @@ def add_alternate_rule(entrance, rule):
entrance.access_rule = lambda state: old_rule(state) or rule(state)
def create_owg_connections(player, world, connections, rule):
for entrance, parent_region, target_region in connections:
def create_owg_connections(player, world, connections, default_rule):
for entrance, parent_region, target_region, *rule_override in connections:
parent = world.get_region(parent_region, player)
target = world.get_region(target_region, player)
connection = Entrance(player, entrance, parent)
parent.exits.append(connection)
connection.connect(target)
rule = rule_override[0] if len(rule_override) > 0 else default_rule
connection.access_rule = rule

View File

@ -81,42 +81,44 @@ class TestDarkWorld(TestVanillaOWG):
["Pyramid", False, []],
["Pyramid", False, [], ['Beat Agahnim 1', 'Moon Pearl', 'Magic Mirror']],
["Pyramid", False, [], ['Beat Agahnim 1', 'Moon Pearl', 'Pegasus Boots', 'Flute', 'Lamp']],
["Pyramid", False, [], ['Beat Agahnim 1', 'Moon Pearl', 'Pegasus Boots']],
["Pyramid", True, ['Moon Pearl', 'Pegasus Boots']],
["Pyramid", True, ['Magic Mirror', 'Pegasus Boots']],
["Pyramid", True, ['Magic Mirror', 'Flute']],
["Pyramid", True, ['Magic Mirror', 'Progressive Glove', 'Lamp']],
["Pyramid", True, ['Beat Agahnim 1']],
["Pyramid", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Pyramid", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Flippers']],
["Pyramid Fairy - Left", False, []],
["Pyramid Fairy - Left", False, [], ['Pegasus Boots', 'Moon Pearl', 'Flute', 'Lamp']],
["Pyramid Fairy - Left", False, [], ['Pegasus Boots', 'Moon Pearl', 'Beat Agahnim 1']],
["Pyramid Fairy - Left", False, [], ['Pegasus Boots', 'Moon Pearl', 'Crystal 5']],
["Pyramid Fairy - Left", False, [], ['Pegasus Boots', 'Moon Pearl', 'Crystal 6']],
["Pyramid Fairy - Left", False, [], ['Magic Mirror', 'Crystal 5']],
["Pyramid Fairy - Left", False, [], ['Magic Mirror', 'Crystal 6']],
["Pyramid Fairy - Left", False, [], ['Magic Mirror', 'Moon Pearl']],
["Pyramid Fairy - Left", True, ['Magic Mirror', 'Pegasus Boots']],
["Pyramid Fairy - Left", True, ['Flute', 'Magic Mirror']],
["Pyramid Fairy - Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror']],
["Pyramid Fairy - Left", True, ['Moon Pearl', 'Crystal 5', 'Crystal 6', 'Beat Agahnim 1', 'Hammer']],
["Pyramid Fairy - Left", True, ['Moon Pearl', 'Crystal 5', 'Crystal 6', 'Progressive Glove', 'Hammer']],
["Pyramid Fairy - Left", True, ['Moon Pearl', 'Crystal 5', 'Crystal 6', 'Beat Agahnim 1', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror']],
["Pyramid Fairy - Left", True, ['Moon Pearl', 'Crystal 5', 'Crystal 6', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot', 'Magic Mirror']],
["Pyramid Fairy - Left", True, ['Moon Pearl', 'Crystal 5', 'Crystal 6', 'Beat Agahnim 1', 'Flippers', 'Hookshot', 'Magic Mirror']],
["Pyramid Fairy - Left", True, ['Crystal 5', 'Crystal 6', 'Beat Agahnim 1', 'Flute', 'Magic Mirror']],
["Pyramid Fairy - Left", True, ['Crystal 5', 'Crystal 6', 'Beat Agahnim 1', 'Progressive Glove', 'Lamp', 'Magic Mirror']],
["Pyramid Fairy - Right", False, []],
["Pyramid Fairy - Right", False, [], ['Pegasus Boots', 'Moon Pearl', 'Flute', 'Lamp']],
["Pyramid Fairy - Right", False, [], ['Pegasus Boots', 'Moon Pearl', 'Beat Agahnim 1']],
["Pyramid Fairy - Right", False, [], ['Pegasus Boots', 'Moon Pearl', 'Crystal 5']],
["Pyramid Fairy - Right", False, [], ['Pegasus Boots', 'Moon Pearl', 'Crystal 6']],
["Pyramid Fairy - Right", False, [], ['Magic Mirror', 'Crystal 5']],
["Pyramid Fairy - Right", False, [], ['Magic Mirror', 'Crystal 6']],
["Pyramid Fairy - Right", False, [], ['Magic Mirror', 'Moon Pearl']],
["Pyramid Fairy - Right", True, ['Magic Mirror', 'Pegasus Boots']],
["Pyramid Fairy - Right", True, ['Flute', 'Magic Mirror']],
["Pyramid Fairy - Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror']],
["Pyramid Fairy - Right", True, ['Moon Pearl', 'Crystal 5', 'Crystal 6', 'Beat Agahnim 1', 'Hammer']],
["Pyramid Fairy - Right", True, ['Moon Pearl', 'Crystal 5', 'Crystal 6', 'Progressive Glove', 'Hammer']],
["Pyramid Fairy - Right", True, ['Moon Pearl', 'Crystal 5', 'Crystal 6', 'Beat Agahnim 1', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror']],
["Pyramid Fairy - Right", True, ['Moon Pearl', 'Crystal 5', 'Crystal 6', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot', 'Magic Mirror']],
["Pyramid Fairy - Right", True, ['Moon Pearl', 'Crystal 5', 'Crystal 6', 'Beat Agahnim 1', 'Flippers', 'Hookshot', 'Magic Mirror']],
["Pyramid Fairy - Right", True, ['Crystal 5', 'Crystal 6', 'Beat Agahnim 1', 'Flute', 'Magic Mirror']],
["Pyramid Fairy - Right", True, ['Crystal 5', 'Crystal 6', 'Beat Agahnim 1', 'Progressive Glove', 'Lamp', 'Magic Mirror']],
["Ganon", False, []],
["Ganon", False, [], ['Moon Pearl']],