Refactor OWG rules to keep them separate from no glitches.

Add logic for superbunny access to Sahasrahla's closet (with boots)
Add logic for superbunny through Two Brother's House (with boots)
Fix superbunny logic for spiral cave and mini moldorm cave - requires a sword
Add logic for waterwalking into Waterfall Cave
Add clip to Ice Palace entrance (requires boots, pearl and flippers)
Add extra clips to inverted owg logic - PoD and Dark Lake Hylia Ledge are now in logic with just boots
Add clip to flute point 6 into inverted owg logic - some light world locations are now accessible with boots and mitts, but no pearl or mirror.
This commit is contained in:
compiling
2020-05-07 18:07:53 +10:00
parent bd7325b03d
commit 441f59d6ea
12 changed files with 382 additions and 484 deletions

136
Rules.py
View File

@@ -1,8 +1,9 @@
import collections
import logging
import OWGSets
from BaseClasses import CollectionState, RegionType
import OverworldGlitchRules
from BaseClasses import RegionType
from Items import ItemFactory
from OverworldGlitchRules import overworld_glitches_rules
def set_rules(world, player):
@@ -44,6 +45,7 @@ def set_rules(world, player):
# 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)
fake_flipper_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.')
@@ -431,7 +433,7 @@ def default_rules(world, 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))
set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has_Pearl(player) and (state.has('Hammer', player) or state.can_lift_rocks(player))) # Fake Flippers
set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has_Pearl(player) and (state.has('Hammer', player) or state.can_lift_rocks(player))) # Move this to NE Dark World
set_rule(world.get_entrance('Village of Outcasts Heavy Rock', player), lambda state: state.has_Pearl(player) and state.can_lift_heavy_rocks(player))
set_rule(world.get_entrance('Hype Cave', player), lambda state: state.has_Pearl(player)) # bomb required
set_rule(world.get_entrance('Brewery', player), lambda state: state.has_Pearl(player)) # bomb required
@@ -482,21 +484,6 @@ def default_rules(world, player):
swordless_rules(world, 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)
else:
for exit in OWGSets.get_mirror_clip_spots_lw():
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
@@ -669,16 +656,35 @@ def no_glitches_rules(world, player):
set_rule(world.get_entrance('Northeast Light World Warp', 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))
set_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: state.has('Flippers', player))
set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has('Flippers', player) and (state.has('Hammer', player) or state.can_lift_rocks(player)))
set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has('Flippers', 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 (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 fake_flipper_rules(world, player):
if world.mode[player] != 'inverted':
set_rule(world.get_entrance('Zoras River', player), lambda state: True)
set_rule(world.get_entrance('Lake Hylia Central Island Pier', player), lambda state: True)
set_rule(world.get_entrance('Hobo Bridge', player), lambda state: True)
set_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: state.has_Pearl(player) and state.has('Flippers', 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))
else:
set_rule(world.get_entrance('Zoras River', player), lambda state: state.has_Pearl(player))
set_rule(world.get_entrance('Lake Hylia Central Island Pier', player), lambda state: state.has_Pearl(player))
set_rule(world.get_entrance('Lake Hylia Island Pier', player), lambda state: state.has_Pearl(player))
set_rule(world.get_entrance('Lake Hylia Warp', player), lambda state: state.has_Pearl(player))
set_rule(world.get_entrance('Northeast Light World Warp', player), lambda state: state.has_Pearl(player))
set_rule(world.get_entrance('Hobo Bridge', player), lambda state: state.has_Pearl(player))
set_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: state.has('Flippers', player))
set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: True)
set_rule(world.get_entrance('Dark Lake Hylia Ledge Drop', player), lambda state: True)
set_rule(world.get_entrance('East Dark World Pier', player), lambda state: True) #todo: Ice Palace exit
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']
@@ -753,52 +759,6 @@ def add_conditional_lamps(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():
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.
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(spot, 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('Flute', player) or state.can_boots_clip_lw(player)) and state.can_lift_heavy_rocks(player))
set_rule(world.get_entrance('Turtle Rock Teleporter', player), lambda state: (state.can_boots_clip_lw(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')
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))
@@ -1367,12 +1327,17 @@ def set_bunny_rules(world, player):
if world.logic[player] == 'owglitches':
if region.name == 'Swamp Palace (Entrance)':
return lambda state: state.has_Pearl(player)
if region.name in OWGSets.get_invalid_bunny_revival_dungeons():
if region.name in OverworldGlitchRules.get_invalid_bunny_revival_dungeons():
return lambda state: state.has_Mirror(player) or state.has_Pearl(player)
if region.type == RegionType.Dungeon:
return lambda state: True
if (((location is None or location.name not in OWGSets.get_superbunny_accessible_locations())
or (connecting_entrance is not None and connecting_entrance.name in OWGSets.get_invalid_bunny_revival_dungeons()))
if region.name in OverworldGlitchRules.get_sword_required_superbunny_mirror_regions():
return lambda state: state.has_Mirror(player) and state.has_sword(player) or state.has_Pearl(player)
if (region.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_regions()
or location is not None and location.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_locations()):
return lambda state: state.has_Mirror(player) and state.has_Boots(player) or state.has_Pearl(player)
if (((location is None or location.name not in OverworldGlitchRules.get_superbunny_accessible_locations())
or (connecting_entrance is not None and connecting_entrance.name in OverworldGlitchRules.get_invalid_bunny_revival_dungeons()))
and not region.is_light_world):
return lambda state: state.has_Pearl(player)
else:
@@ -1402,8 +1367,13 @@ def set_bunny_rules(world, player):
seen.add(new_region)
if not new_region.is_light_world:
# For OWG, establish superbunny and revival rules.
if world.logic[player] == 'owglitches' and entrance.name not in OWGSets.get_invalid_mirror_bunny_entrances_dw():
if location is not None and location.name in OWGSets.get_superbunny_accessible_locations():
if world.logic[player] == 'owglitches' and entrance.name not in OverworldGlitchRules.get_invalid_mirror_bunny_entrances_dw():
if region.name in OverworldGlitchRules.get_sword_required_superbunny_mirror_regions():
possible_options.append(lambda state: path_to_access_rule(new_path, entrance) and state.has_Mirror(player) and state.has_sword(player))
elif (region.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_regions()
or location is not None and location.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_locations()):
possible_options.append(lambda state: path_to_access_rule(new_path, entrance) and state.has_Mirror(player) and state.has_Boots(player))
elif location is not None and location.name in OverworldGlitchRules.get_superbunny_accessible_locations():
if new_region.name == 'Superbunny Cave (Bottom)' or region.name == 'Kakariko Well (top)':
possible_options.append(lambda state: path_to_access_rule(new_path, entrance))
else:
@@ -1437,19 +1407,20 @@ def set_bunny_rules(world, player):
if entrance.player == player and entrance.connected_region.is_dark_world:
if world.logic[player] == 'owglitches':
if entrance.connected_region.type == RegionType.Dungeon:
if entrance.parent_region.type != RegionType.Dungeon and entrance.connected_region.name in OWGSets.get_invalid_bunny_revival_dungeons():
if entrance.parent_region.type != RegionType.Dungeon and entrance.connected_region.name in OverworldGlitchRules.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))
for location in entrance.connected_region.locations:
if world.logic[player] == 'owglitches' and entrance.name in OWGSets.get_invalid_mirror_bunny_entrances_dw():
if world.logic[player] == 'owglitches' and entrance.name in OverworldGlitchRules.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))
#todo: combine this with set_bunny_rules
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.
@@ -1469,12 +1440,12 @@ def set_inverted_bunny_rules(world, player):
if world.logic[player] == 'owglitches':
if region.name == 'Swamp Palace (Entrance)':
return lambda state: state.has_Pearl(player)
if region.name in OWGSets.get_invalid_bunny_revival_dungeons():
if region.name in OverworldGlitchRules.get_invalid_bunny_revival_dungeons():
return lambda state: state.has_Mirror(player) or state.has_Pearl(player)
if region.type == RegionType.Dungeon:
return lambda state: True
if (((location is None or location.name not in OWGSets.get_superbunny_accessible_locations())
or (connecting_entrance is not None and connecting_entrance.name in OWGSets.get_invalid_bunny_revival_dungeons()))
if (((location is None or location.name not in OverworldGlitchRules.get_superbunny_accessible_locations())
or (connecting_entrance is not None and connecting_entrance.name in OverworldGlitchRules.get_invalid_bunny_revival_dungeons()))
and not region.is_dark_world):
return lambda state: state.has_Pearl(player)
else:
@@ -1503,8 +1474,13 @@ def set_inverted_bunny_rules(world, player):
seen.add(new_region)
if not new_region.is_dark_world:
# For OWG, establish superbunny and revival rules.
if world.logic[player] == 'owglitches' and entrance.name not in OWGSets.get_invalid_mirror_bunny_entrances_lw():
if location is not None and location.name in OWGSets.get_superbunny_accessible_locations():
if world.logic[player] == 'owglitches' and entrance.name not in OverworldGlitchRules.get_invalid_mirror_bunny_entrances_lw():
if region.name in OverworldGlitchRules.get_sword_required_superbunny_mirror_regions():
possible_options.append(lambda state: path_to_access_rule(new_path, entrance) and state.has_Mirror(player) and state.has_sword(player))
elif (region.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_regions()
or location is not None and location.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_locations()):
possible_options.append(lambda state: path_to_access_rule(new_path, entrance) and state.has_Mirror(player) and state.has_Boots(player))
elif location is not None and location.name in OverworldGlitchRules.get_superbunny_accessible_locations():
if new_region.name == 'Superbunny Cave (Bottom)' or region.name == 'Kakariko Well (top)':
possible_options.append(lambda state: path_to_access_rule(new_path, entrance))
else:
@@ -1538,13 +1514,13 @@ def set_inverted_bunny_rules(world, player):
if entrance.player == player and entrance.connected_region.is_light_world:
if world.logic[player] == 'owglitches':
if entrance.connected_region.type == RegionType.Dungeon:
if entrance.parent_region.type != RegionType.Dungeon and entrance.connected_region.name in OWGSets.get_invalid_bunny_revival_dungeons():
if entrance.parent_region.type != RegionType.Dungeon and entrance.connected_region.name in OverworldGlitchRules.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))
for location in entrance.connected_region.locations:
if world.logic[player] == 'owglitches' and entrance.name in OWGSets.get_invalid_mirror_bunny_entrances_lw():
if world.logic[player] == 'owglitches' and entrance.name in OverworldGlitchRules.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: