Merge branch 'KrisDavie-hmg_logic' into DoorDevVolatile

This commit is contained in:
aerinon
2023-12-27 13:00:44 -07:00
committed by codemann8
parent 7c5a5f39fe
commit b5795e4bf2
35 changed files with 1168 additions and 154 deletions

View File

@@ -133,7 +133,7 @@ class World(object):
set_player_attr('can_access_trock_front', None)
set_player_attr('can_access_trock_big_chest', None)
set_player_attr('can_access_trock_middle', None)
set_player_attr('fix_fake_world', logic[player] not in ['owglitches', 'nologic']
set_player_attr('fix_fake_world', logic[player] not in ['owglitches', 'hybridglitches', 'nologic']
or shuffle[player] in ['lean', 'swapped', 'crossed', 'insanity'])
set_player_attr('mapshuffle', False)
set_player_attr('compassshuffle', False)
@@ -603,6 +603,42 @@ class CollectionState(object):
self.placing_items = None
# self.trace = None
def can_reach_from(self, spot, start, player=None):
old_state = self.copy()
# old_state.path = {old_state.world.get_region(start, player)}
old_state.stale[player] = False
old_state.reachable_regions[player] = dict()
old_state.blocked_connections[player] = dict()
rrp = old_state.reachable_regions[player]
bc = old_state.blocked_connections[player]
# init on first call - this can't be done on construction since the regions don't exist yet
start = self.world.get_region(start, player)
if start in self.reachable_regions[player]:
rrp[start] = self.reachable_regions[player][start]
for conn in start.exits:
bc[conn] = self.blocked_connections[player][conn]
else:
rrp[start] = CrystalBarrier.Orange
for conn in start.exits:
bc[conn] = CrystalBarrier.Orange
queue = deque(old_state.blocked_connections[player].items())
old_state.traverse_world(queue, rrp, bc, player)
if old_state.world.key_logic_algorithm[player] == 'default':
unresolved_events = [x for y in old_state.reachable_regions[player] for x in y.locations
if x.event and x.item and (x.item.smallkey or x.item.bigkey or x.item.advancement)
and x not in old_state.locations_checked and x.can_reach(old_state)]
unresolved_events = old_state._do_not_flood_the_keys(unresolved_events)
if len(unresolved_events) == 0:
old_state.check_key_doors_in_dungeons(rrp, player)
if self.world.get_region(spot, player) in rrp:
return True
else:
return False
def update_reachable_regions(self, player):
self.stale[player] = False
rrp = self.reachable_regions[player]
@@ -1020,7 +1056,7 @@ class CollectionState(object):
# try to resolve a name
if resolution_hint == 'Location':
spot = self.world.get_location(spot, player)
elif resolution_hint in ['Entrance', 'OWEdge', 'OWTerrain', 'Ledge', 'Portal', 'Whirlpool', 'Mirror', 'Flute']:
elif resolution_hint in ['Entrance', 'OWEdge', 'OWTerrain', 'OpenTerrain', 'Ledge', 'OWG', 'Portal', 'Whirlpool', 'Mirror', 'Flute']:
spot = self.world.get_entrance(spot, player)
else:
# default to Region
@@ -1169,6 +1205,12 @@ class CollectionState(object):
def can_lift_rocks(self, player):
return self.has('Power Glove', player) or self.has('Titans Mitts', player)
def can_bomb_clip(self, region, player: int) -> bool:
return self.is_not_bunny(region, player) and self.has('Pegasus Boots', player) and self.can_use_bombs(player)
def can_dash_clip(self, region, player: int) -> bool:
return self.is_not_bunny(region, player) and self.has('Pegasus Boots', player)
def has_bottle(self, player):
return self.bottle_count(player) > 0
@@ -1348,6 +1390,9 @@ class CollectionState(object):
def can_superbunny_mirror_with_sword(self, player):
return self.has_Mirror(player) and self.has_sword(player)
def can_bunny_pocket(self, player):
return self.has_Boots(player) and (self.has_Mirror(player) or self.has_bottle(player))
def collect(self, item, event=False, location=None):
if location:
self.locations_checked.add(location)
@@ -1631,7 +1676,7 @@ class Entrance(object):
if region not in explored_regions:
explored_regions[region] = path
for exit in region.exits:
if exit.connected_region and (not ignore_ledges or exit.spot_type != 'Ledge') \
if exit.connected_region and (not ignore_ledges or exit.spot_type not in ['Ledge', 'OWG']) \
and exit.name not in ['Dig Game To Ledge Drop'] \
and exit.access_rule(state):
if exit.connected_region == destination:
@@ -3422,7 +3467,7 @@ er_mode = {"vanilla": 0, "simple": 1, "restricted": 2, "full": 3, "crossed": 4,
'lean': 9, "dungeonsfull": 7, "dungeonssimple": 6, "swapped": 10}
# byte 1: LLLW WSS? (logic, mode, sword)
logic_mode = {"noglitches": 0, "minorglitches": 1, "nologic": 2, "owglitches": 3, "majorglitches": 4}
logic_mode = {"noglitches": 0, "minorglitches": 1, "nologic": 2, "owglitches": 3, "majorglitches": 4, "hybridglitches": 5}
world_mode = {"open": 0, "standard": 1, "inverted": 2}
sword_mode = {"random": 0, "assured": 1, "swordless": 2, "vanilla": 3}

View File

@@ -814,7 +814,7 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits, player, must
# Keeps track of entrances that cannot be used to access each exit / cave
invalid_cave_connections = defaultdict(set)
# if world.logic[player] in ['owglitches', 'nologic']:
# if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']:
# import OverworldGlitchRules
# for entrance in OverworldGlitchRules.get_non_mandatory_exits(world, player):
# if entrance in must_be_exits:

View File

@@ -175,6 +175,9 @@ def valid_key_placement(item, location, key_pool, collection_state, world):
if dungeon:
if dungeon.name not in item.name and (dungeon.name != 'Hyrule Castle' or 'Escape' not in item.name):
return True
# Small key and big key in Swamp and Hera are placed without logic
if world.logic[item.player] == 'hybridglitches' and dungeon.name in ['Tower of Hera', 'Swamp Palace'] and dungeon.name in item.name:
return True
key_logic = world.key_logic[item.player][dungeon.name]
unplaced_keys = len([x for x in key_pool if x.name == key_logic.small_key_name and x.player == item.player])
prize_loc = None
@@ -420,7 +423,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]
or world.logic[player] in ['owglitches', 'nologic']):
or world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']):
continue
gt_count, total_count = calc_trash_locations(world, player)
scale_factor = .75 * (world.crystals_needed_for_gt[player] / 7)

View File

@@ -327,6 +327,11 @@ def generate_itempool(world, player):
for _ in range(0, amt):
pool.append('Rupees (20)')
if world.logic[player] == 'hybridglitches' and world.pottery[player] not in ['none', 'cave']:
# In HMG force swamp smalls in pots to allow getting out of swamp palace
placed_items['Swamp Palace - Trench 1 Pot Key'] = 'Small Key (Swamp Palace)'
placed_items['Swamp Palace - Pot Row Pot Key'] = 'Small Key (Swamp Palace)'
start_inventory = list(world.precollected_items)
for item in precollected_items:
world.push_precollected(ItemFactory(item, player))
@@ -1056,7 +1061,7 @@ def get_pool_core(world, player, progressive, shuffle, difficulty, treasure_hunt
return random.choice([True, False]) if progressive == 'random' else progressive == 'on'
# provide boots to boots glitch dependent modes
if logic in ['owglitches', 'nologic']:
if logic in ['owglitches', 'hybridglitches', 'nologic']:
precollected_items.append('Pegasus Boots')
pool.remove('Pegasus Boots')
pool.extend(['Rupees (20)'])
@@ -1362,12 +1367,12 @@ def make_custom_item_pool(world, player, progressive, shuffle, difficulty, timer
start_inventory = [x for x in world.precollected_items if x.player == player]
if not start_inventory:
if world.logic[player] in ['owglitches', 'nologic']:
if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic'] and all(x.name != 'Pegasus Boots' for x in start_inventory):
precollected_items.append('Pegasus Boots')
if 'Pegasus Boots' in pool:
pool.remove('Pegasus Boots')
pool.append('Rupees (20)')
if world.swords[player] == 'assured':
if world.swords[player] == 'assured' and all(' Sword' not in x.name for x in start_inventory):
precollected_items.append('Progressive Sword')
if 'Progressive Sword' in pool:
pool.remove('Progressive Sword')
@@ -1503,7 +1508,7 @@ def make_customizer_pool(world, player):
sphere_0 = world.customizer.get_start_inventory()
no_start_inventory = not sphere_0 or not sphere_0[player]
init_equip = [] if no_start_inventory else sphere_0[player]
if (world.logic[player] in ['owglitches', 'nologic']
if (world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']
and (no_start_inventory or all(x != 'Pegasus Boots' for x in init_equip))):
precollected_items.append('Pegasus Boots')
if 'Pegasus Boots' in pool:

View File

@@ -2106,6 +2106,22 @@ def validate_key_placement(key_layout, world, player):
if i.player == player and i.name == smallkey_name:
keys_outside += 1
if world.logic[player] == 'hybridglitches':
# Swamp keylogic
if smallkey_name.endswith('(Swamp Palace)'):
swamp_entrance = world.get_location('Swamp Palace - Entrance', player)
# Swamp small not vanilla
if swamp_entrance.item is None or (swamp_entrance.item.name != smallkey_name or swamp_entrance.item.player != player):
mire_keylayout = world.key_layout[player]['Misery Mire']
mire_smallkey_name = dungeon_keys[mire_keylayout.sector.name]
# Check if any mire keys are in swamp (excluding entrance), if none then add one to keys_outside
mire_keys_in_swamp = sum([1 if x.item.name == mire_smallkey_name else 0 for x in key_layout.item_locations if x.item is not None and x != swamp_entrance])
if mire_keys_in_swamp == 0:
keys_outside +=1
# Mire keylogic
if smallkey_name.endswith('(Tower of Hera)'):
# TODO: Make sure that mire medallion isn't in hera basement, or if it is, the small key is available downstairs
big_key_outside = True
for code, counter in key_layout.key_counters.items():
if len(counter.child_doors) == 0:
continue

23
Main.py
View File

@@ -28,6 +28,7 @@ from Fill import distribute_items_restrictive, promote_dungeon_items, fill_dunge
from Fill import dungeon_tracking
from Fill import sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression, lock_shop_locations, set_prize_drops
from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops, fill_specific_items, create_farm_locations
from UnderworldGlitchRules import create_hybridmajor_connections, create_hybridmajor_connectors
from Utils import output_path, parse_player_names
from source.item.District import init_districts
@@ -78,6 +79,11 @@ def main(args, seed=None, fish=None):
seeded = True
world.customizer.adjust_args(args)
world = init_world(args, fish)
for i in zip(args.logic.values(), args.door_shuffle.values()):
if i[0] == 'hybridglitches' and i[1] != 'vanilla':
raise RuntimeError(BabelFish().translate("cli","cli","hybridglitches.door.shuffle"))
if seed is None:
random.seed(None)
world.seed = random.randint(0, 999999999)
@@ -166,6 +172,8 @@ def main(args, seed=None, fish=None):
create_dungeons(world, player)
adjust_locations(world, player)
place_bosses(world, player)
if world.logic[player] in ('nologic', 'hybridglitches'):
create_hybridmajor_connections(world, player)
if any(world.potshuffle.values()):
logger.info(world.fish.translate("cli", "cli", "shuffling.pots"))
@@ -191,6 +199,8 @@ def main(args, seed=None, fish=None):
for player in range(1, world.players + 1):
link_entrances_new(world, player)
if world.logic[player] in ('nologic', 'hybridglitches'):
create_hybridmajor_connectors(world, player)
logger.info(world.fish.translate("cli", "cli", "shuffling.prep"))
@@ -594,8 +604,10 @@ def copy_world(world):
for player in range(1, world.players + 1):
create_regions(ret, player)
update_world_regions(ret, player)
if world.logic[player] in ('owglitches', 'nologic'):
if world.logic[player] in ('owglitches', 'hybridglitches', 'nologic'):
create_owg_connections(ret, player)
if world.logic[player] in ('nologic', 'hybridglitches'):
create_hybridmajor_connections(ret, player)
create_dynamic_exits(ret, player)
create_dungeon_regions(ret, player)
create_owedges(ret, player)
@@ -703,6 +715,8 @@ def copy_world(world):
for player in range(1, world.players + 1):
categorize_world_regions(ret, player)
create_farm_locations(ret, player)
if world.logic[player] in ('nologic', 'hybridglitches'):
create_hybridmajor_connectors(ret, player)
set_rules(ret, player)
return ret
@@ -782,8 +796,10 @@ def copy_world_premature(world, player):
create_regions(ret, player)
update_world_regions(ret, player)
if world.logic[player] in ('owglitches', 'nologic'):
if world.logic[player] in ('owglitches', 'hybridglitches', 'nologic'):
create_owg_connections(ret, player)
if world.logic[player] in ('nologic', 'hybridglitches'):
create_hybridmajor_connections(ret, player)
create_dynamic_exits(ret, player)
create_dungeon_regions(ret, player)
create_owedges(ret, player)
@@ -844,6 +860,9 @@ def copy_world_premature(world, player):
for portal in world.dungeon_portals[player]:
connect_portal(portal, ret, player)
if world.logic[player] in ('nologic', 'hybridglitches'):
create_hybridmajor_connectors(ret, player)
set_rules(ret, player)
return ret

View File

@@ -1558,23 +1558,39 @@ OWExitTypes = {
'Ice Lake Southeast Water Drop',
'Bomber Corner Waterfall Water Drop'
],
'OWTerrain': ['Lost Woods Bush (West)',
'Lost Woods Bush (East)',
'Old Man Drop Off',
'OpenTerrain': ['Old Man Drop Off',
'Spectacle Rock Approach',
'Spectacle Rock Leave',
'DM Hammer Bridge (West)',
'DM Hammer Bridge (East)',
'Floating Island Bridge (East)',
'Fairy Ascension Rocks (Inner)',
'DM Broken Bridge (West)',
'DM Broken Bridge (East)',
'Spiral Mimic Bridge (West)',
'Spiral Mimic Bridge (East)',
'Spiral Ledge Approach',
'Mimic Ledge Approach',
'Fairy Ascension Rocks (Outer)',
'Floating Island Bridge (West)',
'Graveyard Ladder (Bottom)',
'Graveyard Ladder (Top)',
'Hyrule Castle Main Gate (South)',
'Hyrule Castle Main Gate (North)',
'Stone Bridge (Northbound)',
'Stone Bridge (Southbound)',
'Checkerboard Ledge Approach',
'Checkerboard Ledge Leave',
'Cave 45 Approach',
'Cave 45 Leave',
'Middle Aged Man',
'Desert Pass Ladder (South)',
'Desert Pass Ladder (North)',
'GT Approach',
'GT Leave',
],
'OWTerrain': ['Lost Woods Bush (West)',
'Lost Woods Bush (East)',
'DM Hammer Bridge (West)',
'DM Hammer Bridge (East)',
'Fairy Ascension Rocks (Inner)',
'DM Broken Bridge (West)',
'DM Broken Bridge (East)',
'Fairy Ascension Rocks (Outer)',
'TR Pegs Ledge Entry',
'TR Pegs Ledge Leave',
'Mountain Pass Rock (Outer)',
@@ -1587,8 +1603,6 @@ OWExitTypes = {
'Lost Woods Pass Rock (North)',
'Lost Woods Pass Rock (South)',
'Kings Grave Rocks (Outer)',
'Graveyard Ladder (Bottom)',
'Graveyard Ladder (Top)',
'Kings Grave Rocks (Inner)',
'River Bend Water Drop',
'River Bend West Pier',
@@ -1602,12 +1616,10 @@ OWExitTypes = {
'Kakariko Yard Bush (South)',
'Kakariko Southwest Bush (South)',
'Kakariko Yard Bush (North)',
'Hyrule Castle Main Gate (South)',
'Hyrule Castle East Rock (Inner)',
'Hyrule Castle Southwest Bush (North)',
'Hyrule Castle Southwest Bush (South)',
'Hyrule Castle Courtyard Bush (South)',
'Hyrule Castle Main Gate (North)',
'Hyrule Castle Courtyard Bush (North)',
'Hyrule Castle East Rock (Outer)',
'Wooden Bridge Bush (South)',
@@ -1615,17 +1627,11 @@ OWExitTypes = {
'Blacksmith Ledge Peg (West)',
'Blacksmith Ledge Peg (East)',
'Maze Race Game',
'Stone Bridge (Northbound)',
'Stone Bridge (Southbound)',
'Desert Statue Move',
'Checkerboard Ledge Approach',
'Desert Ledge Rocks (Outer)',
'Desert Ledge Rocks (Inner)',
'Checkerboard Ledge Leave',
'Flute Boy Bush (South)',
'Cave 45 Approach',
'Flute Boy Bush (North)',
'Cave 45 Leave',
'C Whirlpool Rock (Bottom)',
'C Whirlpool Rock (Top)',
'C Whirlpool Pegs (Outer)',
@@ -1644,19 +1650,14 @@ OWExitTypes = {
'Lake Hylia Water D Approach',
'Lake Hylia Water D Leave',
'Ice Cave Pier',
'Desert Pass Ladder (South)',
'Desert Pass Rocks (North)',
'Desert Pass Rocks (South)',
'Desert Pass Ladder (North)',
'Middle Aged Man',
'Octoballoon Water Drop',
'Octoballoon Pier',
'Skull Woods Rock (East)',
'Skull Woods Rock (West)',
'Skull Woods Forgotten Bush (West)',
'Skull Woods Forgotten Bush (East)',
'GT Approach',
'GT Leave',
'East Dark Death Mountain Bushes',
'Bumper Cave Rock (Outer)',
'Bumper Cave Rock (Inner)',

View File

@@ -5,102 +5,86 @@ Helper functions to deliver entrance/exit/region sets to OWG rules.
from BaseClasses import Entrance
from OWEdges import OWTileRegions
# Cave regions that superbunny can get through - but only with a sword.
sword_required_superbunny_mirror_regions = ["Spiral Cave (Top)"]
def get_sword_required_superbunny_mirror_regions():
"""
Cave regions that superbunny can get through - but only with a sword.
"""
yield 'Spiral Cave (Top)'
# Cave regions that superbunny can get through - but only with boots.
boots_required_superbunny_mirror_regions = ["Two Brothers House"]
def get_boots_required_superbunny_mirror_regions():
"""
Cave regions that superbunny can get through - but only with boots.
"""
yield 'Two Brothers House'
# Cave locations that superbunny can access - but only with boots.
boots_required_superbunny_mirror_locations = [
"Sahasrahla's Hut - Left",
"Sahasrahla's Hut - Middle",
"Sahasrahla's Hut - Right",
]
def get_boots_required_superbunny_mirror_locations():
"""
Cave locations that superbunny can access - but only with boots.
"""
yield 'Sahasrahla\'s Hut - Left'
yield 'Sahasrahla\'s Hut - Middle'
yield 'Sahasrahla\'s Hut - Right'
# Entrances that can't be superbunny-mirrored into.
invalid_mirror_bunny_entrances = [
"Hype Cave",
"Bonk Fairy (Dark)",
"Thieves Town",
"Hammer Peg Cave",
"Brewery",
"Hookshot Cave",
"Dark Lake Hylia Ledge Fairy",
"Dark Lake Hylia Ledge Spike Cave",
"Palace of Darkness",
"Misery Mire",
"Turtle Rock",
"Bonk Rock Cave",
"Bonk Fairy (Light)",
"50 Rupee Cave",
"20 Rupee Cave",
"Checkerboard Cave",
"Light Hype Fairy",
"Waterfall of Wishing",
"Light World Bomb Hut",
"Mini Moldorm Cave",
"Ice Rod Cave",
"Sanctuary Grave",
"Kings Grave",
"Sanctuary Grave",
"Hyrule Castle Secret Entrance Drop",
"Skull Woods Second Section Hole",
"Skull Woods First Section Hole (North)",
]
# Interior locations that can be accessed with superbunny state.
superbunny_accessible_locations = [
"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",
"Blind's Hideout - Left",
"Blind's Hideout - Right",
"Blind's Hideout - Far Left",
"Blind's Hideout - Far Right",
"Kakariko Well - Left",
"Kakariko Well - Middle",
"Kakariko Well - Right",
"Kakariko Well - Bottom",
"Kakariko Tavern",
"Library",
"Spiral Cave",
] + boots_required_superbunny_mirror_locations
# TODO: Add pottery locations
def get_invalid_mirror_bunny_entrances():
"""
Entrances that can't be superbunny-mirrored into.
"""
yield 'Skull Woods Final Section'
yield 'Hype Cave'
yield 'Bonk Fairy (Dark)'
yield 'Thieves Town'
yield 'Hammer Peg Cave'
yield 'Brewery'
yield 'Hookshot Cave'
yield 'Dark Lake Hylia Ledge Fairy'
yield 'Dark Lake Hylia Ledge Spike Cave'
yield 'Palace of Darkness'
yield 'Misery Mire'
yield 'Turtle Rock'
yield 'Bonk Rock Cave'
yield 'Bonk Fairy (Light)'
yield '50 Rupee Cave'
yield '20 Rupee Cave'
yield 'Checkerboard Cave'
yield 'Light Hype Fairy'
yield 'Waterfall of Wishing'
yield 'Light World Bomb Hut'
yield 'Mini Moldorm Cave'
yield 'Ice Rod Cave'
yield 'Sanctuary Grave'
yield 'Kings Grave'
yield 'Sanctuary Grave'
yield 'Hyrule Castle Secret Entrance Drop'
yield 'Skull Woods Second Section Hole'
yield 'Skull Woods First Section Hole (North)'
def get_superbunny_accessible_locations():
"""
Interior locations that can be accessed with superbunny state.
"""
yield 'Waterfall of Wishing - Left'
yield 'Waterfall of Wishing - Right'
yield 'King\'s Tomb'
yield 'Floodgate'
yield 'Floodgate Chest'
yield 'Cave 45'
yield 'Bonk Rock Cave'
yield 'Brewery'
yield 'C-Shaped House'
yield 'Chest Game'
yield 'Mire Shed - Left'
yield 'Mire Shed - Right'
yield 'Secret Passage'
yield 'Ice Rod Cave'
yield 'Pyramid Fairy - Left'
yield 'Pyramid Fairy - Right'
yield 'Superbunny Cave - Top'
yield 'Superbunny Cave - Bottom'
yield 'Blind\'s Hideout - Left'
yield 'Blind\'s Hideout - Right'
yield 'Blind\'s Hideout - Far Left'
yield 'Blind\'s Hideout - Far Right'
yield 'Kakariko Well - Left'
yield 'Kakariko Well - Middle'
yield 'Kakariko Well - Right'
yield 'Kakariko Well - Bottom'
yield 'Kakariko Tavern'
yield 'Library'
yield 'Spiral Cave'
for location in get_boots_required_superbunny_mirror_locations():
yield location
def get_non_mandatory_exits(world, player):
"""
Entrances that can be reached with full equipment using overworld glitches and don't need to be an exit.
@@ -295,6 +279,7 @@ def overworld_glitches_rules(world, player):
#set_owg_rules(player, world, get_glitched_speed_drops_dw(world, player), lambda state: state.can_get_glitched_speed_dw(player))
# Mirror clip spots.
# TODO: Should this also require can_boots_clip
set_owg_rules(player, world, get_mirror_clip_spots(world, player), lambda state: state.has_Mirror(player))
# Mirror offset spots.
@@ -319,6 +304,18 @@ def overworld_glitches_rules(world, player):
add_additional_rule(world.get_entrance('Tree Line Water Clip', player), lambda state: state.has('Flippers', player))
add_additional_rule(world.get_entrance('Dark Tree Line Water Clip', player), lambda state: state.has('Flippers', player))
# Bunny pocket
if not world.is_tile_swapped(0x00, player):
add_alternate_rule(world.get_entrance("Skull Woods Final Section", player), lambda state: state.can_bunny_pocket(player) and state.has("Fire Rod", player))
if world.is_tile_swapped(0x05, player):
add_alternate_rule(world.get_entrance("DM Hammer Bridge (West)", player), lambda state: state.can_bunny_pocket(player) and state.has("Hammer", player))
add_alternate_rule(world.get_entrance("DM Hammer Bridge (East)", player), lambda state: state.can_bunny_pocket(player) and state.has("Hammer", player))
if not world.is_tile_swapped(0x18, player):
add_alternate_rule(world.get_entrance("Bush Yard Pegs (Inner)", player), lambda state: state.can_bunny_pocket(player) and state.has("Hammer", player))
add_alternate_rule(world.get_entrance("Bush Yard Pegs (Outer)", player), lambda state: state.can_bunny_pocket(player) and state.has("Hammer", player))
if world.is_tile_swapped(0x22, player):
add_alternate_rule(world.get_entrance("Blacksmith Ledge Peg (West)", player), lambda state: state.can_bunny_pocket(player) and state.has("Hammer", player))
def add_alternate_rule(entrance, rule):
old_rule = entrance.access_rule
@@ -335,7 +332,7 @@ def create_no_logic_connections(player, world, connections):
parent = world.get_region(parent_region, player)
target = world.get_region(target_region, player)
connection = Entrance(player, entrance, parent)
connection.spot_type = 'Ledge'
connection.spot_type = 'OWG'
parent.exits.append(connection)
connection.connect(target)
@@ -387,6 +384,8 @@ boots_clips_local = [ # (name, from_region, to_region)
('Maze Race Item Get Ledge Clip', 'Maze Race Area', 'Maze Race Prize'),
#('Hobo Water Clip', 'Stone Bridge South Area', 'Stone Bridge Water'), # TODO: Doesn't work with OW Free Terrain
('Tree Line Water Clip', 'Tree Line Area', 'Tree Line Water'), #requires flippers
('Dark Tree Line Water Clip', 'Dark Tree Line Area', 'Dark Tree Line Water'), #requires flippers

View File

@@ -200,7 +200,7 @@ def link_overworld(world, player):
categorize_world_regions(world, player)
if world.logic[player] in ('owglitches', 'nologic'):
if world.logic[player] in ('owglitches', 'hybridglitches', 'nologic'):
create_owg_connections(world, player)
# crossed shuffle
@@ -1486,7 +1486,7 @@ def build_accessible_region_list(world, start_region, player, build_copy_world=F
elif exit.connected_region.name not in explored_regions \
and (exit.connected_region.type == region.type
or exit.name in OWExitTypes['OWEdge'] or (cross_world and exit.name in (OWExitTypes['Portal'] + OWExitTypes['Mirror']))) \
and (not region_rules or exit.access_rule(blank_state)) and (not ignore_ledges or exit.name not in OWExitTypes['Ledge']):
and (not region_rules or exit.access_rule(blank_state)) and (not ignore_ledges or exit.name not in OWExitTypes['Ledge', 'OWG']):
explore_region(exit.connected_region.name, exit.connected_region)
if build_copy_world:

6
Rom.py
View File

@@ -1365,9 +1365,9 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_byte(0x180211, gametype) # Game type
warningflags = 0x00 # none
if world.logic[player] in ['owglitches', 'nologic']:
if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']:
warningflags |= 0x20
if world.logic[player] in ['minorglitches', 'owglitches', 'nologic']:
if world.logic[player] in ['minorglitches', 'owglitches', 'hybridglitches', 'nologic']:
warningflags |= 0x40
rom.write_byte(0x180212, warningflags) # Warning flags
@@ -1599,7 +1599,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_byte(0x1800A3, 0x01) # enable correct world setting behaviour after agahnim kills
rom.write_byte(0x1800A4, 0x01 if world.logic[player] != 'nologic' else 0x00) # enable POD EG fix
rom.write_byte(0x180042, 0x01 if world.save_and_quit_from_boss else 0x00) # Allow Save and Quit after boss kill
rom.write_byte(0x180358, 0x01 if (world.logic[player] in ['owglitches', 'nologic']) else 0x00)
rom.write_byte(0x180358, 0x01 if (world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']) else 0x00)
# remove shield from uncle
rom.write_bytes(0x6D253, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])

View File

@@ -9,6 +9,7 @@ from Dungeons import dungeon_table
from RoomData import DoorKind
from OWEdges import OWExitTypes
from OverworldGlitchRules import overworld_glitches_rules
from UnderworldGlitchRules import underworld_glitches_rules
def set_rules(world, player):
@@ -32,7 +33,7 @@ def set_rules(world, player):
logging.getLogger('').info('Minor Glitches may be buggy still. No guarantee for proper logic checks.')
no_glitches_rules(world, player)
fake_flipper_rules(world, player)
elif world.logic[player] == 'owglitches':
elif world.logic[player] in ['owglitches', 'hybridglitches']:
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.
@@ -84,6 +85,9 @@ def set_rules(world, player):
if not world.is_copied_world:
set_bunny_rules(world, player, world.mode[player] == 'inverted')
# These rules go here because they overwrite/add to some of the above rules
if world.logic[player] == 'hybridglitches':
underworld_glitches_rules(world, player)
def mirrorless_path_to_location(world, startName, targetName, player):
# If Agahnim is defeated then the courtyard needs to be accessible without using the mirror for the mirror offset glitch.
@@ -845,6 +849,9 @@ def global_rules(world, player):
set_rule(world.get_entrance('GT Crystal Circles to Ranged Crystal', player), lambda state: state.can_hit_crystal_through_barrier(player) or state.has_blunt_weapon(player) or state.has('Cane of Byrna', player)) # or state.has_beam_sword(player)
add_key_logic_rules(world, player)
if world.logic[player] == 'hybridglitches':
add_hmg_key_logic_rules(world, player)
# End of door rando rules.
if world.restrict_boss_items[player] != 'none':
@@ -1644,6 +1651,8 @@ def find_rules_for_zelda_delivery(world, player):
def set_bunny_rules(world, player, inverted):
# 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.
all_single_exit_dungeons = ['Eastern Palace', 'Tower of Hera', 'Castle Tower', 'Palace of Darkness', 'Swamp Palace', 'Thieves Town', 'Ice Palace', 'Misery Mire', 'Ganons Tower']
hmg_single_exit_dungeons = [d for d in all_single_exit_dungeons if d not in ['Tower of Hera', 'Misery Mire', 'Thieves Town']]
bunny_impassable_caves = ['Bumper Cave (top)', 'Bumper Cave (bottom)', 'Two Brothers House',
'Hookshot Cave (Middle)', 'Pyramid', 'Spiral Cave (Top)', 'Fairy Ascension Cave (Drop)']
bunny_accessible_locations = ['Link\'s Uncle', 'Sahasrahla', 'Sick Kid', 'Lost Woods Hideout', 'Lumberjack Tree',
@@ -1668,6 +1677,9 @@ def set_bunny_rules(world, player, inverted):
'Take - Any # 3 Item 1', 'Take - Any # 3 Item 2',
'Take - Any # 4 Item 1', 'Take - Any # 4 Item 2',
]
bunny_pocket_entrances = ['Skull Woods Final Section', 'Bush Yard Pegs (Inner)', 'Bush Yard Pegs (Outer)',
'DM Hammer Bridge (West)', 'DM Hammer Bridge (East)', 'Blacksmith Ledge Peg (West)'
]
def path_to_access_rule(path, entrance):
return lambda state: state.can_reach(entrance) and all(rule_func(state) for rule_func in path)
@@ -1688,12 +1700,31 @@ def set_bunny_rules(world, player, inverted):
else:
return region.is_light_world
# Is it possible to do bunny pocket here
def can_bunny_pocket_to(world, entrance_name, player):
def can_activate_bunny_pocket(region):
explored_regions.append(region.name)
for ent in region.entrances:
if (is_link(ent.parent_region) or
(ent.parent_region.type == RegionType.Dungeon and ent.parent_region.name in bunny_revivable_entrances)):
return True
for ent in region.entrances:
if ent.spot_type in ['OWEdge', 'Ledge', 'OpenTerrain'] and ent.parent_region.name not in explored_regions:
if can_activate_bunny_pocket(ent.parent_region):
return True
return False
entrance = world.get_entrance(entrance_name, player)
explored_regions = []
return can_activate_bunny_pocket(entrance.parent_region)
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 world.logic[player] in ['owglitches', 'hybridglitches']:
if region.type != RegionType.Dungeon \
and (location is None or location.name not in OverworldGlitchRules.get_superbunny_accessible_locations()) \
and (location is None or location.name not in OverworldGlitchRules.superbunny_accessible_locations) \
and not is_link(region):
return lambda state: state.has_Pearl(player)
else:
@@ -1723,14 +1754,18 @@ def set_bunny_rules(world, player, inverted):
new_path = path + [entrance.access_rule]
seen_sets.add(frozenset(new_seen))
if not is_link(new_region):
if world.logic[player] == 'owglitches':
if world.logic[player] in ['owglitches', 'hybridglitches']:
# Is this a bunny pocketable entrance?
if region.type == RegionType.Dungeon and new_region.type != RegionType.Dungeon:
if entrance.name in OverworldGlitchRules.get_invalid_mirror_bunny_entrances():
if entrance.name in OverworldGlitchRules.invalid_mirror_bunny_entrances:
continue
if entrance.name in bunny_pocket_entrances and not can_bunny_pocket_to(world, entrance.name, player):
continue
if entrance.name in drop_dungeon_entrances:
lobby = entrance.connected_region
else:
lobby = next(exit.connected_region for exit in current.exits if exit.connected_region.type == RegionType.Dungeon)
portal_regions = [world.get_region(reg, player) for reg in region.dungeon.regions if reg.endswith('Portal')]
lobby = next(reg.connected_region for portal_reg in portal_regions for reg in portal_reg.exits if reg.name.startswith('Enter '))
if lobby.name in bunny_revivable_entrances:
possible_options.append(path_to_access_rule(new_path, entrance))
elif lobby.name in superbunny_revivable_entrances:
@@ -1739,21 +1774,23 @@ def set_bunny_rules(world, player, inverted):
possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player) and state.has_sword(player)], entrance))
continue
elif region.type == RegionType.Cave and new_region.type != RegionType.Cave:
if entrance.name in OverworldGlitchRules.get_invalid_mirror_bunny_entrances():
if entrance.name in OverworldGlitchRules.invalid_mirror_bunny_entrances:
continue
if region.name in OverworldGlitchRules.get_sword_required_superbunny_mirror_regions():
if entrance.name in bunny_pocket_entrances and not can_bunny_pocket_to(world, entrance.name, player):
continue
if region.name in OverworldGlitchRules.sword_required_superbunny_mirror_regions:
possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player) and state.has_sword(player)], entrance))
elif region.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_regions():
elif region.name in OverworldGlitchRules.boots_required_superbunny_mirror_regions:
possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player) and state.has_Boots(player)], entrance))
elif location and location.name in OverworldGlitchRules.get_superbunny_accessible_locations():
if location.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_locations():
elif location and location.name in OverworldGlitchRules.superbunny_accessible_locations:
if location.name in OverworldGlitchRules.boots_required_superbunny_mirror_locations:
possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player) and state.has_Boots(player)], entrance))
elif region.name == 'Kakariko Well (top)':
possible_options.append(path_to_access_rule(new_path, entrance))
else:
possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player)], entrance))
continue
elif region.name == 'Superbunny Cave (Top)' and new_region.name == 'Superbunny Cave (Bottom)' and location and location.name in OverworldGlitchRules.get_superbunny_accessible_locations():
elif region.name == 'Superbunny Cave (Top)' and new_region.name == 'Superbunny Cave (Bottom)' and location and location.name in OverworldGlitchRules.superbunny_accessible_locations:
possible_options.append(path_to_access_rule(new_path, entrance))
else:
continue
@@ -1767,7 +1804,6 @@ def set_bunny_rules(world, player, inverted):
# Add requirements for bunny-impassible caves if they occur in the light world
for region in [world.get_region(name, player) for name in bunny_impassable_caves]:
if not is_bunny(region):
continue
rule = get_rule_to_add(region)
@@ -1802,6 +1838,11 @@ def set_bunny_rules(world, player, inverted):
continue
add_rule(location, get_rule_to_add(region, location))
for ent_name in bunny_pocket_entrances:
bunny_exit = world.get_entrance(ent_name, player)
if bunny_exit.connected_region and is_bunny(bunny_exit.parent_region) and not can_bunny_pocket_to(world, ent_name, player):
add_rule(bunny_exit, lambda state: state.has_Pearl(player))
drop_dungeon_entrances = {
"Sewer Drop",
@@ -1929,6 +1970,11 @@ bunny_impassible_if_trapped = {
'GT Speed Torch WN', 'Ice Lobby SE'
}
def add_hmg_key_logic_rules(world, player):
for toh_loc in world.key_logic[player]['Tower of Hera'].bk_restricted:
set_always_allow(world.get_location(toh_loc.name, player), allow_big_key_in_big_chest('Big Key (Tower of Hera)', player))
set_always_allow(world.get_location('Swamp Palace - Entrance', player), allow_big_key_in_big_chest('Big Key (Swamp Palace)', player))
def add_key_logic_rules(world, player):
key_logic = world.key_logic[player]

278
UnderworldGlitchRules.py Normal file
View File

@@ -0,0 +1,278 @@
from BaseClasses import Entrance
import Rules
from OverworldGlitchRules import create_no_logic_connections
kikiskip_spots = [("Kiki Skip", "Spectacle Rock Cave (Bottom)", "Palace of Darkness Portal")]
mireheraswamp_spots = [
("Mire to Hera Clip", "Mire Torches Top", "Hera Portal"),
("Hera to Swamp Clip", "Mire Torches Top", "Swamp Portal"),
]
icepalace_spots = [("Ice Lobby Clip", "Ice Portal", "Ice Bomb Drop")]
thievesdesert_spots = [
("Thieves to Desert Clip", "Thieves Attic", "Desert West Portal"),
("Thieves to Desert Clip", "Thieves Attic", "Desert South Portal"),
("Thieves to Desert Clip", "Thieves Attic", "Desert East Portal"),
]
specrock_spots = [("Spec Rock Clip", "Spectacle Rock Cave (Peak)", "Spectacle Rock Cave (Top)")]
paradox_spots = [("Paradox Front Teleport", "Paradox Cave Front", "Paradox Cave Chest Area")]
# We need to make connectors at a separate time from the connections, because of how dungeons are linked to regions
kikiskip_connectors = [("Kiki Skip", "Spectacle Rock Cave (Bottom)", "Palace of Darkness Exit")]
mireheraswamp_connectors = [
("Mire to Hera Clip", "Mire Torches Top", "Tower of Hera Exit"),
("Mire to Hera Clip", "Mire Torches Top", "Swamp Palace Exit"),
]
thievesdesert_connectors = [
("Thieves to Desert Clip", "Thieves Attic", "Desert Palace Exit (West)"),
("Thieves to Desert Clip", "Thieves Attic", "Desert Palace Exit (South)"),
("Thieves to Desert Clip", "Thieves Attic", "Desert Palace Exit (East)"),
]
specrock_connectors = [
("Spec Rock Clip", "Spectacle Rock Cave (Peak)", "Spectacle Rock Cave Exit (Top)"),
("Spec Rock Clip", "Spectacle Rock Cave (Peak)", "Spectacle Rock Cave Exit"),
]
# Create connections between dungeons/locations
def create_hybridmajor_connections(world, player):
for spots in [
kikiskip_spots,
mireheraswamp_spots,
icepalace_spots,
thievesdesert_spots,
specrock_spots,
paradox_spots,
]:
create_no_logic_connections(player, world, spots)
# Turn dungeons into connectors
def create_hybridmajor_connectors(world, player):
for connectors in [
kikiskip_connectors,
mireheraswamp_connectors,
thievesdesert_connectors,
specrock_connectors,
]:
new_connectors = [(connector[0], connector[1], world.get_entrance(connector[2], player).connected_region) for connector in connectors]
create_no_logic_connections(player, world, new_connectors)
# For some entrances, we need to fake having pearl, because we're in fake DW/LW.
# This creates a copy of the input state that has Moon Pearl.
def fake_pearl_state(state, player):
if state.has("Moon Pearl", player):
return state
fake_state = state.copy()
fake_state.prog_items["Moon Pearl", player] += 1
return fake_state
# Sets the rules on where we can actually go using this clip.
# Behavior differs based on what type of ER shuffle we're playing.
def dungeon_reentry_rules(world, player, clip: Entrance, dungeon_region: str, dungeon_exit: str):
fix_dungeon_exits = world.fix_palaceofdarkness_exit[player]
fix_fake_worlds = world.fix_fake_world[player]
dungeon_entrance = [r for r in world.get_region(dungeon_region, player).entrances if r.name != clip.name][0]
if not fix_dungeon_exits: # vanilla, simple, restricted, dungeonssimple; should never have fake worlds fix
# Dungeons are only shuffled among themselves. We need to check SW, MM, and AT because they can't be reentered trivially.
# entrance doesn't exist until you fire rod it from the other side
if dungeon_entrance.name == "Skull Woods Final Section":
Rules.set_rule(clip, lambda state: False)
elif dungeon_entrance.name == "Misery Mire":
if world.swords[player] == "swordless":
Rules.add_rule(clip, lambda state: state.has_misery_mire_medallion(player))
else:
Rules.add_rule(clip, lambda state: state.has_sword(player) and state.has_misery_mire_medallion(player))
elif dungeon_entrance.name == "Agahnims Tower":
Rules.add_rule(
clip,
lambda state: state.has("Cape", player)
or state.has_beam_sword(player)
or state.has("Beat Agahnim 1", player),
)
# Then we set a restriction on exiting the dungeon, so you can't leave unless you got in normally.
Rules.add_rule(world.get_entrance(dungeon_exit, player), lambda state: dungeon_entrance.can_reach(state))
elif not fix_fake_worlds: # full, dungeonsfull; fixed dungeon exits, but no fake worlds fix
# Entry requires the entrance's requirements plus a fake pearl, but you don't gain logical access to the surrounding region.
Rules.add_rule(clip, lambda state: dungeon_entrance.access_rule(fake_pearl_state(state, player)))
# exiting restriction
Rules.add_rule(world.get_entrance(dungeon_exit, player), lambda state: dungeon_entrance.can_reach(state))
# Otherwise, the shuffle type is lean, lite, crossed, or insanity; all of these do not need additional rules on where we can go,
# since the clip links directly to the exterior region.
def underworld_glitches_rules(world, player):
# Ice Palace Entrance Clip, needs bombs or cane of somaria to exit bomb drop room
Rules.add_rule(
world.get_entrance("Ice Bomb Drop SE", player),
lambda state: state.can_dash_clip(world.get_region("Ice Lobby", player), player)
and (state.can_use_bombs(player) or state.has("Cane of Somaria", player)),
combine="or",
)
# Kiki Skip
kks = world.get_entrance("Kiki Skip", player)
Rules.set_rule(kks, lambda state: state.can_bomb_clip(kks.parent_region, player))
dungeon_reentry_rules(world, player, kks, "Palace of Darkness Portal", "Palace of Darkness Exit")
# Mire -> Hera -> Swamp
def mire_clip(state):
return state.can_reach("Mire Torches Top", "Region", player) and state.can_dash_clip(
world.get_region("Mire Torches Top", player), player
)
def hera_clip(state):
return state.can_reach("Hera 4F", "Region", player) and state.can_dash_clip(
world.get_region("Hera 4F", player), player
)
Rules.add_rule(
world.get_entrance("Hera Startile Corner NW", player),
lambda state: mire_clip(state) and state.has("Big Key (Misery Mire)", player),
combine="or",
)
Rules.add_rule(
world.get_location("Tower of Hera - Big Chest", player),
lambda state: mire_clip(state) and state.has("Big Key (Misery Mire)", player),
combine="or",
)
mire_to_hera = world.get_entrance("Mire to Hera Clip", player)
mire_to_swamp = world.get_entrance("Hera to Swamp Clip", player)
Rules.set_rule(mire_to_hera, mire_clip)
Rules.set_rule(mire_to_swamp, lambda state: mire_clip(state) and state.has("Flippers", player))
# Using the entrances for various ER types. Hera -> Swamp never matters because you can only logically traverse with the mire keys
dungeon_reentry_rules(world, player, mire_to_hera, "Hera Lobby", "Tower of Hera Exit")
dungeon_reentry_rules(world, player, mire_to_swamp, "Swamp Lobby", "Swamp Palace Exit")
# We need to set _all_ swamp doors to be openable with mire keys, otherwise the small key can't be behind them - 6 keys because of Pots
# Flippers required for all of these doors to prevent locks when flooding
for door in [
"Swamp Trench 1 Approach ES",
"Swamp Hammer Switch SW",
"Swamp Entrance Down Stairs",
"Swamp Pot Row WS",
"Swamp Trench 1 Key Ledge NW",
"Swamp Hub WN",
"Swamp Hub North Ledge N",
"Swamp Crystal Switch EN",
"Swamp Push Statue S",
"Swamp Waterway NW",
"Swamp T SW",
]:
Rules.add_rule(
world.get_entrance(door, player),
lambda state: mire_clip(state)
and state.has("Small Key (Misery Mire)", player, count=6)
and state.has("Flippers", player),
combine="or",
)
# Rules.add_rule(world.get_entrance(door, player), lambda state: mire_clip(state) and state.has('Flippers', player), combine="or")
Rules.add_rule(
world.get_location("Trench 1 Switch", player), lambda state: mire_clip(state) or hera_clip(state), combine="or"
)
# Build the rule for SP moat.
# We need to be able to s+q to old man, then go to either Mire or Hera at either Hera or GT.
# First we require a certain type of entrance shuffle, then build the rule from its pieces.
if not world.swamp_patch_required[player]:
if world.shuffle[player] in [
"vanilla",
"dungeonssimple",
"dungeonsfull",
"dungeonscrossed",
]:
rule_map = {
"Mire Portal": (lambda state: state.can_reach("Mire Torches Top", "Entrance", player)),
"Hera Portal": (lambda state: state.can_reach("Hera Startile Corner NW", "Entrance", player)),
}
inverted = world.mode[player] == "inverted"
def hera_rule(state):
return (state.has("Moon Pearl", player) or not inverted) and rule_map.get(
world.get_entrance("Tower of Hera", player).connected_region.name, lambda state: False
)(state)
def gt_rule(state):
return (state.has("Moon Pearl", player) or inverted) and rule_map.get(
world.get_entrance(("Ganons Tower"), player).connected_region.name, lambda state: False
)(state)
def mirrorless_moat_rule(state):
return (
state.can_reach("Old Man S&Q", "Entrance", player)
and state.has("Flippers", player)
and mire_clip(state)
and (hera_rule(state) or gt_rule(state))
)
Rules.add_rule(
world.get_entrance("Swamp Lobby Moat", player), lambda state: mirrorless_moat_rule(state), combine="or"
)
# Thieves -> Desert
Rules.add_rule(
world.get_entrance("Thieves to Desert Clip", player),
lambda state: state.can_dash_clip(world.get_region("Thieves Attic", player), player),
)
dungeon_reentry_rules(
world,
player,
world.get_entrance("Thieves to Desert Clip", player),
"Desert West Portal",
"Desert Palace Exit (West)",
)
dungeon_reentry_rules(
world,
player,
world.get_entrance("Thieves to Desert Clip", player),
"Desert South Portal",
"Desert Palace Exit (South)",
)
dungeon_reentry_rules(
world,
player,
world.get_entrance("Thieves to Desert Clip", player),
"Desert East Portal",
"Desert Palace Exit (East)",
)
# Collecting left chests in Paradox Cave using a dash clip -> dash citrus, 1f right, teleport up
paradox_left_chests = ["Paradox Cave Lower - Far Left", "Paradox Cave Lower - Left", "Paradox Cave Lower - Middle"]
for location in paradox_left_chests:
Rules.add_rule(
world.get_location(location, player),
lambda state: state.can_dash_clip(world.get_location(location, player).parent_region, player),
"or",
)
# Collecting right chests in Paradox Cave using a dash clip on left side -> dash citrus, 1f right, teleport up, then hitting the switch
paradox_right_chests = ["Paradox Cave Lower - Right", "Paradox Cave Lower - Far Right"]
for location in paradox_right_chests:
Rules.add_rule(
world.get_location(location, player),
lambda state: (
state.can_dash_clip(world.get_location(location, player).parent_region, player)
and state.can_hit_crystal(player)
),
"or",
)

View File

@@ -23,6 +23,7 @@
"noglitches",
"minorglitches",
"owglitches",
"hybridglitches",
"nologic"
]
},

View File

@@ -56,7 +56,8 @@
"building.collection.spheres": "Building up collection spheres",
"building.calculating.spheres": "Calculated sphere %i, containing %i of %i progress items.",
"building.final.spheres": "Calculated final sphere %i, containing %i of %i progress items.",
"old.python.version": "Door Rando may have issues with python versions earlier than 3.7. Detected version: %s"
"old.python.version": "Door Rando may have issues with python versions earlier than 3.7. Detected version: %s",
"hybridglitches.door.shuffle": "Hybrid Major Glitches is not currently compatible with Door Shuffle."
},
"help": {
"lang": [ "App Language, if available, defaults to English" ],
@@ -64,11 +65,13 @@
"bps": [ "Output BPS patches instead of ROMs"],
"logic": [
"Select Enforcement of Item Requirements. (default: %(default)s)",
"No Glitches: No Glitch knowledge required.",
"Minor Glitches: May require Fake Flippers, Bunny Revival",
" and Dark Room Navigation.",
"No Logic: Distribute items without regard for",
" item requirements."
"No Glitches: No Glitch knowledge required.",
"Minor Glitches: May require Fake Flippers, Bunny Revival",
" and Dark Room Navigation.",
"Overworld Glitches: May require overworld clips and teleports.",
"Hybrid Major Glitches: May require underworld clips.",
"No Logic: Distribute items without regard for",
" item requirements."
],
"mode": [
"Select game mode. (default: %(default)s)",

View File

@@ -263,6 +263,7 @@
"randomizer.item.logiclevel.noglitches": "No Glitches",
"randomizer.item.logiclevel.minorglitches": "Minor Glitches",
"randomizer.item.logiclevel.owglitches": "Overworld Glitches",
"randomizer.item.logiclevel.hybridglitches": "Hybrid Major Glitches",
"randomizer.item.logiclevel.nologic": "No Logic",
"randomizer.item.goal": "Goal",

View File

@@ -21,6 +21,7 @@
"noglitches",
"minorglitches",
"owglitches",
"hybridglitches",
"nologic"
]
},

View File

@@ -1317,7 +1317,7 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit):
invalid_connections = Must_Exit_Invalid_Connections.copy()
invalid_cave_connections = defaultdict(set)
if avail.world.logic[avail.player] in ['owglitches', 'nologic']:
if avail.world.logic[avail.player] in ['owglitches', 'hybridglitches', 'nologic']:
import OverworldGlitchRules
for entrance in OverworldGlitchRules.get_non_mandatory_exits(avail.world, avail.player):
invalid_connections[entrance] = set()

View File

@@ -46,8 +46,9 @@ def roll_settings(weights):
ret.algorithm = get_choice('algorithm')
glitch_map = {'none': 'noglitches', 'no_logic': 'nologic', 'owglitches': 'owglitches',
'owg': 'owglitches', 'minorglitches': 'minorglitches'}
glitch_map = {'none': 'noglitches', 'minorglitches': 'minorglitches', 'no_logic': 'nologic',
'hmg': 'hybridglitches', 'hybridglitches': 'hybridglitches',
'owg': 'owglitches', 'owglitches': 'owglitches'}
glitches_required = get_choice('glitches_required')
if glitches_required is not None:
if glitches_required not in glitch_map.keys():

View File

@@ -0,0 +1,35 @@
meta:
players: 1
settings:
1:
accessibility: items
door_shuffle: vanilla
logic: hybridglitches
mode: open
shuffle: vanilla
doors:
1:
doors:
Hera Beetles WS: Hera Startile Corner ES
Hera Startile Corner ES: Hera Beetles WS
Hera Startile Corner NW: Hera Startile Wide SW
Hera Startile Wide SW: Hera Startile Corner NW
Skull 1 Lobby ES: Skull Map Room WS
Skull 2 West Lobby ES: Skull Small Hall WS
Skull 2 West Lobby NW: Skull X Room SW
Skull Big Chest N: Skull Pull Switch S
Skull Compass Room WS: Skull Left Drop ES
Skull Final Drop WS: Skull Spike Corner ES
Skull Left Drop ES: Skull Compass Room WS
Skull Map Room WS: Skull 1 Lobby ES
Skull Pot Circle WN: Skull Pull Switch EN
Skull Pull Switch EN: Skull Pot Circle WN
Skull Pull Switch S: Skull Big Chest N
Skull Small Hall WS: Skull 2 West Lobby ES
Skull Spike Corner ES: Skull Final Drop WS
Skull X Room SW: Skull 2 West Lobby NW
lobbies: {}
placements:
1:
Ice Palace - Compass Chest: Fire Rod
Ice Palace - Freezor Chest: Bombos

View File

@@ -0,0 +1,42 @@
meta:
players: 1
settings:
1:
accessibility: items
door_shuffle: vanilla
logic: hybridglitches
mode: open
shuffle: vanilla
start_inventory:
1:
- Flippers
- Lamp
- Tempered Sword
- Boss Heart Container
- Boss Heart Container
- Boss Heart Container
doors:
1:
doors:
Hera Beetles WS: Hera Startile Corner ES
Hera Startile Corner ES: Hera Beetles WS
Hera Startile Corner NW: Hera Startile Wide SW
Hera Startile Wide SW: Hera Startile Corner NW
Skull 1 Lobby ES: Skull Map Room WS
Skull 2 West Lobby ES: Skull Small Hall WS
Skull 2 West Lobby NW: Skull X Room SW
Skull Big Chest N: Skull Pull Switch S
Skull Compass Room WS: Skull Left Drop ES
Skull Final Drop WS: Skull Spike Corner ES
Skull Left Drop ES: Skull Compass Room WS
Skull Map Room WS: Skull 1 Lobby ES
Skull Pot Circle WN: Skull Pull Switch EN
Skull Pull Switch EN: Skull Pot Circle WN
Skull Pull Switch S: Skull Big Chest N
Skull Small Hall WS: Skull 2 West Lobby ES
Skull Spike Corner ES: Skull Final Drop WS
Skull X Room SW: Skull 2 West Lobby NW
lobbies: {}
placements:
1:
Swamp Palace - Big Chest: Hammer

View File

@@ -0,0 +1,34 @@
meta:
players: 1
settings:
1:
accessibility: items
door_shuffle: vanilla
logic: hybridglitches
mode: open
shuffle: vanilla
doors:
1:
doors:
Hera Beetles WS: Hera Startile Corner ES
Hera Startile Corner ES: Hera Beetles WS
Hera Startile Corner NW: Hera Startile Wide SW
Hera Startile Wide SW: Hera Startile Corner NW
Skull 1 Lobby ES: Skull Map Room WS
Skull 2 West Lobby ES: Skull Small Hall WS
Skull 2 West Lobby NW: Skull X Room SW
Skull Big Chest N: Skull Pull Switch S
Skull Compass Room WS: Skull Left Drop ES
Skull Final Drop WS: Skull Spike Corner ES
Skull Left Drop ES: Skull Compass Room WS
Skull Map Room WS: Skull 1 Lobby ES
Skull Pot Circle WN: Skull Pull Switch EN
Skull Pull Switch EN: Skull Pot Circle WN
Skull Pull Switch S: Skull Big Chest N
Skull Small Hall WS: Skull 2 West Lobby ES
Skull Spike Corner ES: Skull Final Drop WS
Skull X Room SW: Skull 2 West Lobby NW
lobbies: {}
placements:
1:
Palace of Darkness - Shooter Room: Moon Pearl

View File

@@ -0,0 +1,42 @@
meta:
players: 1
settings:
1:
accessibility: items
door_shuffle: vanilla
logic: hybridglitches
mode: open
shuffle: vanilla
start_inventory:
1:
- Flippers
- Lamp
- Tempered Sword
- Boss Heart Container
- Boss Heart Container
- Boss Heart Container
doors:
1:
doors:
Hera Beetles WS: Hera Startile Corner ES
Hera Startile Corner ES: Hera Beetles WS
Hera Startile Corner NW: Hera Startile Wide SW
Hera Startile Wide SW: Hera Startile Corner NW
Skull 1 Lobby ES: Skull Map Room WS
Skull 2 West Lobby ES: Skull Small Hall WS
Skull 2 West Lobby NW: Skull X Room SW
Skull Big Chest N: Skull Pull Switch S
Skull Compass Room WS: Skull Left Drop ES
Skull Final Drop WS: Skull Spike Corner ES
Skull Left Drop ES: Skull Compass Room WS
Skull Map Room WS: Skull 1 Lobby ES
Skull Pot Circle WN: Skull Pull Switch EN
Skull Pull Switch EN: Skull Pot Circle WN
Skull Pull Switch S: Skull Big Chest N
Skull Small Hall WS: Skull 2 West Lobby ES
Skull Spike Corner ES: Skull Final Drop WS
Skull X Room SW: Skull 2 West Lobby NW
lobbies: {}
placements:
1:
Swamp Palace - Big Chest: Magic Mirror

View File

@@ -0,0 +1,44 @@
meta:
players: 1
settings:
1:
accessibility: items
door_shuffle: vanilla
logic: hybridglitches
mode: open
shuffle: crossed
doors:
1:
doors:
Hera Beetles WS: Hera Startile Corner ES
Hera Startile Corner ES: Hera Beetles WS
Hera Startile Corner NW: Hera Startile Wide SW
Hera Startile Wide SW: Hera Startile Corner NW
Skull 1 Lobby ES: Skull Map Room WS
Skull 2 West Lobby ES: Skull Small Hall WS
Skull 2 West Lobby NW: Skull X Room SW
Skull Big Chest N: Skull Pull Switch S
Skull Compass Room WS: Skull Left Drop ES
Skull Final Drop WS: Skull Spike Corner ES
Skull Left Drop ES: Skull Compass Room WS
Skull Map Room WS: Skull 1 Lobby ES
Skull Pot Circle WN: Skull Pull Switch EN
Skull Pull Switch EN: Skull Pot Circle WN
Skull Pull Switch S: Skull Big Chest N
Skull Small Hall WS: Skull 2 West Lobby ES
Skull Spike Corner ES: Skull Final Drop WS
Skull X Room SW: Skull 2 West Lobby NW
lobbies: {}
entrances:
1:
entrances:
Dark Lake Hylia Ledge Hint: Dark World Hammer Peg Cave
exits:
Links House: Chris Houlihan Room Exit
two-way:
Dark Lake Hylia Ledge Fairy: Palace of Darkness Exit
Lake Hylia Fortune Teller: Spectacle Rock Cave Exit
Links House: Links House Exit
placements:
1:
Peg Cave: Moon Pearl

View File

@@ -0,0 +1,55 @@
meta:
players: 1
settings:
1:
accessibility: items
door_shuffle: vanilla
logic: hybridglitches
mode: open
shuffle: crossed
start_inventory:
1:
- Hookshot
- Lamp
- Hammer
- Magic Mirror
- Tempered Sword
- Boss Heart Container
- Boss Heart Container
- Boss Heart Container
doors:
1:
doors:
Hera Beetles WS: Hera Startile Corner ES
Hera Startile Corner ES: Hera Beetles WS
Hera Startile Corner NW: Hera Startile Wide SW
Hera Startile Wide SW: Hera Startile Corner NW
Skull 1 Lobby ES: Skull Map Room WS
Skull 2 West Lobby ES: Skull Small Hall WS
Skull 2 West Lobby NW: Skull X Room SW
Skull Big Chest N: Skull Pull Switch S
Skull Compass Room WS: Skull Left Drop ES
Skull Final Drop WS: Skull Spike Corner ES
Skull Left Drop ES: Skull Compass Room WS
Skull Map Room WS: Skull 1 Lobby ES
Skull Pot Circle WN: Skull Pull Switch EN
Skull Pull Switch EN: Skull Pot Circle WN
Skull Pull Switch S: Skull Big Chest N
Skull Small Hall WS: Skull 2 West Lobby ES
Skull Spike Corner ES: Skull Final Drop WS
Skull X Room SW: Skull 2 West Lobby NW
lobbies: {}
entrances:
1:
entrances:
Dark Lake Hylia Ledge Hint: Dark World Hammer Peg Cave
exits:
Links House: Chris Houlihan Room Exit
two-way:
Dark Lake Hylia Ledge Fairy: Swamp Palace Exit
Lake Hylia Fortune Teller: Misery Mire Exit
Links House: Links House Exit
placements:
1:
Peg Cave: Moon Pearl

View File

@@ -0,0 +1,44 @@
meta:
players: 1
settings:
1:
accessibility: items
door_shuffle: vanilla
logic: hybridglitches
mode: open
shuffle: vanilla
start_inventory:
1:
- Hookshot
- Lamp
- Hammer
- Magic Mirror
- Tempered Sword
- Boss Heart Container
- Boss Heart Container
- Boss Heart Container
doors:
1:
doors:
Hera Beetles WS: Hera Startile Corner ES
Hera Startile Corner ES: Hera Beetles WS
Hera Startile Corner NW: Hera Startile Wide SW
Hera Startile Wide SW: Hera Startile Corner NW
Skull 1 Lobby ES: Skull Map Room WS
Skull 2 West Lobby ES: Skull Small Hall WS
Skull 2 West Lobby NW: Skull X Room SW
Skull Big Chest N: Skull Pull Switch S
Skull Compass Room WS: Skull Left Drop ES
Skull Final Drop WS: Skull Spike Corner ES
Skull Left Drop ES: Skull Compass Room WS
Skull Map Room WS: Skull 1 Lobby ES
Skull Pot Circle WN: Skull Pull Switch EN
Skull Pull Switch EN: Skull Pot Circle WN
Skull Pull Switch S: Skull Big Chest N
Skull Small Hall WS: Skull 2 West Lobby ES
Skull Spike Corner ES: Skull Final Drop WS
Skull X Room SW: Skull 2 West Lobby NW
lobbies: {}
# placements:
# 1:
# Swamp Palace - Entrance: Boss Heart Container

View File

@@ -0,0 +1,34 @@
meta:
players: 1
settings:
1:
logic: hybridglitches
shuffle: crossed
start_inventory:
1:
- Flippers
- Pegasus Boots
- Progressive Sword
- Hammer
- Progressive Glove
- Progressive Glove
- Fire Rod
- Book of Mudora
- Bottle
- Magic Mirror
- Lamp
advanced_placements:
1:
- type: Verification
item: Moon Pearl
locations:
Pyramid Fairy - Left: True
entrances:
1:
entrances:
Skull Woods Final Section: Pyramid Fairy
two-way:
Chicken House: Two Brothers House Exit (West)
Skull Woods Second Section Door (West): Two Brothers House Exit (East)

View File

@@ -0,0 +1,17 @@
meta:
players: 1
settings:
1:
logic: hybridglitches
start_inventory:
1:
- Flippers
- Moon Pearl
- Progressive Sword
- Hammer
- Progressive Glove
- Progressive Glove
placements:
1:
Ice Palace - Map Chest: Bombos
Ice Palace - Iced T Room: Fire Rod

View File

@@ -0,0 +1,20 @@
meta:
players: 1
settings:
1:
logic: hybridglitches
start_inventory:
1:
- Pegasus Boots
- Moon Pearl
- Progressive Sword
advanced_placements:
1:
- type: Verification
item: Flippers
locations:
Zora's Ledge: True
Hobo: True
Ice Palace - Boss: True
Swamp Palace - Entrance: False

View File

@@ -0,0 +1,29 @@
meta:
players: 1
settings:
1:
logic: owglitches
start_inventory:
1:
- Pegasus Boots
- Moon Pearl
- Progressive Sword
- Flippers
placements:
1:
Peg Cave: Magic Mirror
advanced_placements:
1:
- type: Verification
item: Hammer
locations:
Link's House: True
Magic Bat: False
advanced_placements:
1:
- type: Verification
item: Progressive Glove
locations:
Link's House: True
Ice Palace - Freezor Chest: False

View File

@@ -0,0 +1,26 @@
meta:
players: 1
settings:
1:
logic: hybridglitches
start_inventory:
1:
- Flippers
- Moon Pearl
- Progressive Sword
- Lamp
- Magic Mirror
- Ether
- Quake
- Bombos
advanced_placements:
1:
- type: Verification
item: Big Key (Tower of Hera)
locations:
Tower of Hera - Big Key Chest: True
Tower of Hera - Basement Cage: True
Tower of Hera - Map Chest: True
Tower of Hera - Compass Chest: True
Tower of Hera - Big Chest: True
Tower of Hera - Boss: True

View File

@@ -0,0 +1,26 @@
meta:
players: 1
settings:
1:
logic: hybridglitches
mode: inverted
shuffle: crossed
start_inventory:
1:
- Pegasus Boots
- Progressive Sword
- Hammer
- Fire Rod
placements:
1:
Desert Palace - Boss: Moon Pearl
entrances:
1:
two-way:
Skull Woods Final Section: Desert Palace Exit (West)
Skull Woods Second Section Door (West): Desert Palace Exit (East)
Thieves Town: Thieves Town Exit
Hyrule Castle Entrance (East): Desert Palace Exit (South)
Hyrule Castle Entrance (West): Desert Palace Exit (North)

View File

@@ -0,0 +1,31 @@
meta:
players: 1
settings:
1:
logic: hybridglitches
mode: inverted
start_inventory:
1:
- Flippers
- Progressive Sword
- Progressive Sword
- Book of Mudora
- Lamp
- Magic Mirror
- Ether
- Quake
- Bombos
advanced_placements:
1:
- type: Verification
item: Moon Pearl
locations:
Tower of Hera - Big Chest: True
Desert Palace - Big Chest: True
Eastern Palace - Big Chest: True
Bombos Tablet: True
Cave 45: True

View File

@@ -0,0 +1,48 @@
meta:
players: 1
settings:
1:
logic: hybridglitches
start_inventory:
1:
- Pegasus Boots
- Flippers
- Fire Rod
- Book of Mudora
- Progressive Sword
- Progressive Sword
- Lamp
- Magic Mirror
- Ether
- Quake
- Bombos
advanced_placements:
1:
- type: Verification
item: Moon Pearl
locations:
Skull Woods - Compass Chest: True
Skull Woods - Bridge Room: True
Palace of Darkness - Shooter Room: True
Palace of Darkness - The Arena - Bridge: True
Palace of Darkness - Stalfos Basement: True
Palace of Darkness - Big Key Chest: True
Palace of Darkness - The Arena - Ledge: True
Palace of Darkness - Map Chest: True
Palace of Darkness - Compass Chest: True
Palace of Darkness - Dark Basement - Left: True
Palace of Darkness - Dark Basement - Right: True
Palace of Darkness - Dark Maze - Top: True
Palace of Darkness - Dark Maze - Bottom: True
Palace of Darkness - Big Chest: True
Palace of Darkness - Harmless Hellway: True
Palace of Darkness - Boss: True
Bombos Tablet: True
C-Shaped House: True
Pyramid Fairy - Left: True
Swamp Palace - Entrance: False
Thieves' Town - Map Chest: False

View File

@@ -0,0 +1,25 @@
meta:
players: 1
settings:
1:
logic: hybridglitches
start_inventory:
1:
- Flippers
- Pegasus Boots
- Progressive Sword
- Hammer
- Progressive Glove
- Progressive Glove
- Fire Rod
- Book of Mudora
- Bottle
- Magic Mirror
- Lamp
advanced_placements:
1:
- type: Verification
item: Moon Pearl
locations:
Skull Woods - Bridge Room: True

View File

@@ -0,0 +1,43 @@
meta:
players: 1
settings:
1:
logic: hybridglitches
start_inventory:
1:
- Flippers
- Moon Pearl
- Progressive Sword
- Lamp
- Magic Mirror
- Ether
- Quake
- Bombos
advanced_placements:
1:
- type: Verification
item: Small Key (Swamp Palace)
locations:
Swamp Palace - Entrance: True
Swamp Palace - Map Chest: True
Swamp Palace - Big Chest: True
Swamp Palace - Compass Chest: True
Swamp Palace - West Chest: True
Swamp Palace - Big Key Chest: True
Swamp Palace - Flooded Room - Left: True
Swamp Palace - Flooded Room - Right: True
Swamp Palace - Waterfall Room: True
Swamp Palace - Boss: True
- type: Verification
item: Big Key (Swamp Palace)
locations:
Swamp Palace - Entrance: True
Swamp Palace - Map Chest: True
Swamp Palace - Big Chest: True
Swamp Palace - Compass Chest: True
Swamp Palace - West Chest: True
Swamp Palace - Big Key Chest: True
Swamp Palace - Flooded Room - Left: True
Swamp Palace - Flooded Room - Right: True
Swamp Palace - Waterfall Room: True
Swamp Palace - Boss: True