From 81a8b3a0f555ac890e0ab6c0c694f6f0b4de2fad Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Sat, 12 Oct 2024 16:42:14 +0200 Subject: [PATCH] HMG PoD Region fix + reformatting * Create a region in HMG when PoD entrance isn't pre-opened that limits access to the overworld unless already available * Change rule generations to loop * Check for bomb and dash clips properly --- OverworldShuffle.py | 3 + Regions.py | 15 +++- UnderworldGlitchRules.py | 164 +++++++++++++++++++++++++++------------ 3 files changed, 130 insertions(+), 52 deletions(-) diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 303aeed2..768588d3 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -15,6 +15,9 @@ def link_overworld(world, player): for exitname, regionname in inverted_mandatory_connections: connect_simple(world, exitname, regionname, player) + if world.logic[player] in ['hybridglitches', 'nologic'] and not world.fix_palaceofdarkness_exit[player]: + connect_two_way(world, 'Palace of Darkness HMG Exit', 'Palace of Darkness HMG Entrance', player) + for forward_edge, back_edge in default_connections: connect_two_way(world, forward_edge, back_edge, player) diff --git a/Regions.py b/Regions.py index 6cb44847..e82dd74b 100644 --- a/Regions.py +++ b/Regions.py @@ -191,8 +191,19 @@ def create_regions(world, player): create_dw_region(player, 'Broken Bridge Area', None, ['Broken Bridge Hammer Rock (South)', 'Broken Bridge Water Drop', 'Broken Bridge SW']), create_dw_region(player, 'Broken Bridge Northeast', None, ['Broken Bridge Hammer Rock (North)', 'Broken Bridge Hookshot Gap', 'Broken Bridge Northeast Water Drop', 'Broken Bridge NE']), create_dw_region(player, 'Broken Bridge West', None, ['Broken Bridge West Water Drop', 'Broken Bridge NW']), - create_dw_region(player, 'Broken Bridge Water', None, ['Broken Bridge NC'], 'Dark World', Terrain.Water), - create_dw_region(player, 'Palace of Darkness Area', None, ['Palace of Darkness Hint', 'Palace of Darkness', 'Palace of Darkness SW', 'Palace of Darkness SE']), + create_dw_region(player, 'Broken Bridge Water', None, ['Broken Bridge NC'], 'Dark World', Terrain.Water) + ] + if world.logic[player] in ['hybridglitches', 'nologic'] and not world.fix_palaceofdarkness_exit[player]: + world.regions += [ + create_dw_region(player, 'Palace of Darkness HMG Area', None, ['Palace of Darkness', 'Palace of Darkness HMG Exit']), + create_dw_region(player, 'Palace of Darkness Area', None, ['Palace of Darkness Hint', 'Palace of Darkness SW', 'Palace of Darkness SE', 'Palace of Darkness HMG Entrance']) + ] + else: + world.regions += [ + create_dw_region(player, 'Palace of Darkness Area', None, ['Palace of Darkness Hint', 'Palace of Darkness', 'Palace of Darkness SW', 'Palace of Darkness SE']) + ] + + world.regions += [ create_dw_region(player, 'Hammer Pegs Area', ['Dark Blacksmith Ruins'], ['Hammer Peg Cave', 'Peg Area Rocks (East)']), create_dw_region(player, 'Hammer Pegs Entry', None, ['Peg Area Rocks (West)', 'Hammer Pegs WS']), create_dw_region(player, 'Dark Dunes Area', None, ['Dark Dunes NW', 'Dark Dunes WN', 'Dark Dunes SC']), diff --git a/UnderworldGlitchRules.py b/UnderworldGlitchRules.py index a03797b0..0f4389df 100644 --- a/UnderworldGlitchRules.py +++ b/UnderworldGlitchRules.py @@ -1,3 +1,4 @@ +import functools from BaseClasses import Entrance, DoorType from DoorShuffle import connect_simple_door import Rules @@ -133,19 +134,45 @@ def fake_pearl_state(state, player): # 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 + world, + player, + clip: Entrance, + dungeon_region: str, ): fix_dungeon_exits = world.fix_palaceofdarkness_exit[player] fix_fake_worlds = world.fix_fake_world[player] + all_clips = [ + x[0] + for x in kikiskip_spots + + mirehera_spots + + heraswamp_spots + + icepalace_spots + + thievesdesert_spots + + specrock_spots + + paradox_spots + + kikiskip_connectors + + mirehera_connectors + + heraswamp_connectors + + thievesdesert_connectors + + specrock_connectors + ] + dungeon_entrance = [ r for r in world.get_region(dungeon_region, player).entrances - if r.name != clip.name + if r.name not in all_clips ][0] - if ( - not fix_dungeon_exits - ): # vanilla, simple, restricted, dungeonssimple; should never have fake worlds fix + + dungeon_exit = [ + r + for r in world.get_region(dungeon_region, player).exits + if r.name not in all_clips + ][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 @@ -196,32 +223,85 @@ def dungeon_reentry_rules( 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 + torches = world.get_region("Mire Torches Top", player) + return state.can_dash_clip(torches, player) or ( + state.can_bomb_clip(torches, player) and state.has_fire_source(player) ) + def hera_clip(state): + hera = world.get_region("Hera 4F", player) + return state.can_bomb_clip(hera, player) or state.can_dash_clip(hera, player) + + # We use these plus functool.partial because lambdas don't work in loops properly. + def bomb_clip(state, region, player): + return state.can_bomb_clip(region, player) + + def dash_clip(state, region, player): + return state.can_dash_clip(region, player) + + if not world.fix_palaceofdarkness_exit[player]: + Rules.set_rule( + world.get_entrance("Palace of Darkness HMG Exit", player), + lambda state: state.can_reach(world.get_entrance("Palace of Darkness", player), player), + ) + + + # Bomb clips + for clip in ( + kikiskip_spots + + icepalace_spots + + thievesdesert_spots + + specrock_spots + + kikiskip_connectors + + thievesdesert_connectors + + specrock_connectors + ): + region = world.get_region(clip[1], player) + Rules.set_rule( + world.get_entrance(clip[0], player), + functools.partial(bomb_clip, region=region, player=player), + ) + # Dash clips + for clip in icepalace_spots: + region = world.get_region(clip[1], player) + Rules.add_rule( + world.get_entrance(clip[0], player), + functools.partial(dash_clip, region=region, player=player), + combine="or", + ) + + for spot in kikiskip_spots + thievesdesert_spots: + dungeon_reentry_rules( + world, + player, + world.get_entrance(spot[0], player), + spot[2], + ) + + for connector in kikiskip_connectors + thievesdesert_connectors: + dungeon_reentry_rules( + world, + player, + world.get_entrance(connector[0], player), + world.get_entrance(connector[2], player).parent_region.name, + ) + + for clip in mirehera_spots + mirehera_connectors: + Rules.set_rule( + world.get_entrance(clip[0], player), + lambda state: mire_clip(state), + ) + + # Need to be able to escape by hitting the switch from the back + Rules.set_rule( + world.get_entrance("Ice Bomb Drop Clip", player), + lambda state: ( + state.can_use_bombs(player) or state.has("Cane of Somaria", player) + ), + ) + + # Allow mire big key to be used in Hera Rules.add_rule( world.get_entrance("Hera Startile Corner NW", player), lambda state: mire_clip(state) and state.has("Big Key (Misery Mire)", player), @@ -232,20 +312,10 @@ def underworld_glitches_rules(world, 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) + # This uses the mire clip because it's always expected to come from mire 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" + world.get_entrance("Hera to Swamp Clip", player), + lambda state: mire_clip(state) and state.has("Flippers", player), ) # 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 @@ -269,7 +339,6 @@ def underworld_glitches_rules(world, player): 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), @@ -322,21 +391,16 @@ def underworld_glitches_rules(world, player): lambda state: mirrorless_moat_rule(state), combine="or", ) + desert_exits = ["West", "South", "East"] - for desert_exit in ["East", "South", "West"]: + for desert_exit in desert_exits: Rules.add_rule( world.get_entrance(f"Thieves to Desert {desert_exit} Connector", player), lambda state: state.can_dash_clip( world.get_region("Thieves Attic", player), player ), ) - dungeon_reentry_rules( - world, - player, - world.get_entrance(f"Thieves to Desert {desert_exit} Connector", player), - f"Desert {desert_exit} Portal", - f"Desert Palace Exit ({desert_exit})", - ) + # Collecting left chests in Paradox Cave using a dash clip -> dash citrus, 1f right, teleport up paradox_left_chests = [