From af1892c7d51e485591de34bc583e6489ef2ce71c Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Mon, 16 Dec 2024 00:06:54 +0100 Subject: [PATCH] Revamp HMG logic --- Main.py | 32 +++-- OverworldGlitchRules.py | 14 ++- OverworldShuffle.py | 3 - Regions.py | 15 +-- UnderworldGlitchRules.py | 111 +++--------------- .../hmg/inverted_inaccessible_desert.yaml | 4 + test/suite/hmg/pod_mp.yaml | 13 ++ 7 files changed, 71 insertions(+), 121 deletions(-) create mode 100644 test/suite/hmg/pod_mp.yaml diff --git a/Main.py b/Main.py index 4d806e96f..fcb50142b 100644 --- a/Main.py +++ b/Main.py @@ -27,7 +27,7 @@ from Fill import dungeon_tracking from Fill import sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression, lock_shop_locations from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops, fill_specific_items -from UnderworldGlitchRules import create_hybridmajor_connections, create_hybridmajor_connectors, get_hybridmajor_connector_entrances +from UnderworldGlitchRules import create_hybridmajor_connections, get_hybridmajor_connection_entrances from Utils import output_path, parse_player_names from source.item.FillUtil import create_item_pool_config, massage_item_pool, district_item_pool_config, verify_item_pool_config @@ -222,8 +222,6 @@ def main(args, seed=None, fish=None): world.data_tables[player] = init_data_tables(world, player) place_bosses(world, player) randomize_enemies(world, player) - if world.logic[player] in ('nologic', 'hybridglitches'): - create_hybridmajor_connections(world, player) adjust_locations(world, player) if world.customizer and world.customizer.get_start_inventory(): @@ -264,8 +262,6 @@ def main(args, seed=None, fish=None): link_overworld(world, player) create_dynamic_exits(world, player) 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")) for player in range(1, world.players + 1): @@ -285,6 +281,8 @@ def main(args, seed=None, fish=None): logger.info(world.fish.translate("cli", "cli", "generating.itempool")) for player in range(1, world.players + 1): + if world.logic[player] in ('nologic', 'hybridglitches'): + create_hybridmajor_connections(world, player) generate_itempool(world, player) verify_item_pool_config(world) @@ -533,8 +531,6 @@ def copy_world(world): create_dungeons(ret, player) 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) # there are region references here they must be migrated to preserve integrity @@ -561,7 +557,10 @@ def copy_world(world): # connect copied world copied_locations = {(loc.name, loc.player): loc for loc in ret.get_locations()} # caches all locations - hmg_entrances = get_hybridmajor_connector_entrances() + + # We have to skip these for now. They require both the rest of the entrances _and_ the dungeon portals to be copied first + # We will connect them later + hmg_entrances = get_hybridmajor_connection_entrances() for region in world.regions: copied_region = ret.get_region(region.name, region.player) @@ -621,7 +620,22 @@ def copy_world(world): for player in range(1, world.players + 1): if world.logic[player] in ('nologic', 'hybridglitches'): - create_hybridmajor_connectors(ret, player) + create_hybridmajor_connections(ret, player) + + for region in world.regions: + copied_region = ret.get_region(region.name, region.player) + copied_region.is_light_world = region.is_light_world + copied_region.is_dark_world = region.is_dark_world + copied_region.dungeon = region.dungeon + copied_region.locations = [copied_locations[(location.name, location.player)] for location in region.locations] + for location in copied_region.locations: + location.parent_region = copied_region + for entrance in region.entrances: + if entrance.name not in hmg_entrances: + continue + ret.get_entrance(entrance.name, entrance.player).connect(copied_region) + + for player in range(1, world.players + 1): set_rules(ret, player) return ret diff --git a/OverworldGlitchRules.py b/OverworldGlitchRules.py index 97883dc6b..55e9d8963 100644 --- a/OverworldGlitchRules.py +++ b/OverworldGlitchRules.py @@ -2,7 +2,7 @@ Helper functions to deliver entrance/exit/region sets to OWG rules. """ -from BaseClasses import Entrance +from BaseClasses import Entrance, Region # Cave regions that superbunny can get through - but only with a sword. sword_required_superbunny_mirror_regions = ["Spiral Cave (Top)"] @@ -283,10 +283,18 @@ def add_alternate_rule(entrance, rule): entrance.access_rule = lambda state: old_rule(state) or rule(state) -def create_no_logic_connections(player, world, connections): +def create_no_logic_connections(player, world, connections, connect_external=False): for entrance, parent_region, target_region, *rule_override in connections: parent = world.get_region(parent_region, player) - target = world.get_region(target_region, player) + + if isinstance(target_region, Region): + target_region = target_region.name + + if connect_external and target_region.endswith(" Portal"): + target = world.get_portal(target_region[:-7], player).find_portal_entrance().parent_region + else: + target = world.get_region(target_region, player) + connection = Entrance(player, entrance, parent) parent.exits.append(connection) connection.connect(target) diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 768588d37..303aeed23 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -15,9 +15,6 @@ 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 e82dd74b2..6cb448473 100644 --- a/Regions.py +++ b/Regions.py @@ -191,19 +191,8 @@ 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) - ] - 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, '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, '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 0f4389dfb..beb95ed95 100644 --- a/UnderworldGlitchRules.py +++ b/UnderworldGlitchRules.py @@ -29,42 +29,10 @@ ("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 Connector", "Spectacle Rock Cave (Bottom)", "Palace of Darkness Exit") -] -mirehera_connectors = [ - ("Mire to Hera Connector", "Mire Torches Top", "Tower of Hera Exit") -] -heraswamp_connectors = [ - ("Mire to Swamp Connector", "Mire Torches Top", "Swamp Palace Exit") -] -thievesdesert_connectors = [ - ("Thieves to Desert West Connector", "Thieves Attic", "Desert Palace Exit (West)"), - ( - "Thieves to Desert South Connector", - "Thieves Attic", - "Desert Palace Exit (South)", - ), - ("Thieves to Desert East Connector", "Thieves Attic", "Desert Palace Exit (East)"), -] -specrock_connectors = [ - ( - "Spec Rock Top Connector", - "Spectacle Rock Cave (Peak)", - "Spectacle Rock Cave Exit (Top)", - ), - ( - "Spec Rock Exit Connector", - "Spectacle Rock Cave (Peak)", - "Spectacle Rock Cave Exit", - ), -] - - # Create connections between dungeons/locations def create_hybridmajor_connections(world, player): + fix_fake_worlds = world.fix_fake_world[player] + for spots in [ kikiskip_spots, mirehera_spots, @@ -74,7 +42,7 @@ def create_hybridmajor_connections(world, player): specrock_spots, paradox_spots, ]: - create_no_logic_connections(player, world, spots) + create_no_logic_connections(player, world, spots, connect_external=fix_fake_worlds) # Add the new Ice path (back of bomb drop to front) to the world and model it properly clip_door = create_door(player, "Ice Bomb Drop Clip", DoorType.Logical) @@ -87,39 +55,20 @@ def create_hybridmajor_connections(world, player): ) connect_simple_door(world, "Ice Bomb Drop Clip", "Ice Bomb Drop", player) - -# Turn dungeons into connectors -def create_hybridmajor_connectors(world, player): - for connectors in [ - kikiskip_connectors, - mirehera_connectors, - heraswamp_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) - - -def get_hybridmajor_connector_entrances(): - connectors = [] +def get_hybridmajor_connection_entrances(): + connections = [] for connector in ( - kikiskip_connectors - + mirehera_connectors - + heraswamp_connectors - + thievesdesert_connectors - + specrock_connectors - ): - connectors.append(connector[0]) - return set(connectors) - + kikiskip_spots + + mirehera_spots + + heraswamp_spots + + icepalace_spots + + thievesdesert_spots + + specrock_spots + + paradox_spots + ): + connections.append(connector[0]) + connections.append('Ice Bomb Drop Clip') + return set(connections) # 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. @@ -151,11 +100,6 @@ def dungeon_reentry_rules( + thievesdesert_spots + specrock_spots + paradox_spots - + kikiskip_connectors - + mirehera_connectors - + heraswamp_connectors - + thievesdesert_connectors - + specrock_connectors ] dungeon_entrance = [ @@ -239,23 +183,12 @@ def bomb_clip(state, 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( @@ -279,15 +212,7 @@ def dash_clip(state, region, 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: + for clip in mirehera_spots: Rules.set_rule( world.get_entrance(clip[0], player), lambda state: mire_clip(state), @@ -395,7 +320,7 @@ def mirrorless_moat_rule(state): for desert_exit in desert_exits: Rules.add_rule( - world.get_entrance(f"Thieves to Desert {desert_exit} Connector", player), + world.get_entrance(f"Thieves to Desert {desert_exit} Clip", player), lambda state: state.can_dash_clip( world.get_region("Thieves Attic", player), player ), diff --git a/test/suite/hmg/inverted_inaccessible_desert.yaml b/test/suite/hmg/inverted_inaccessible_desert.yaml index 5e59ca63c..2a55777d6 100644 --- a/test/suite/hmg/inverted_inaccessible_desert.yaml +++ b/test/suite/hmg/inverted_inaccessible_desert.yaml @@ -14,6 +14,8 @@ start_inventory: placements: 1: Desert Palace - Boss: Moon Pearl + Desert Palace - Prize: Green Pendant + Sahasrahla: Magic Mirror entrances: 1: two-way: @@ -22,4 +24,6 @@ entrances: Thieves Town: Thieves Town Exit Hyrule Castle Entrance (East): Desert Palace Exit (South) Hyrule Castle Entrance (West): Desert Palace Exit (North) + entrances: + Agahnims Tower: Pyramid Fairy diff --git a/test/suite/hmg/pod_mp.yaml b/test/suite/hmg/pod_mp.yaml new file mode 100644 index 000000000..19de4b630 --- /dev/null +++ b/test/suite/hmg/pod_mp.yaml @@ -0,0 +1,13 @@ +meta: + players: 1 +settings: + 1: + logic: hybridglitches + mode: open + shuffle: vanilla +start_inventory: + 1: + - Pegasus Boots +placements: + 1: + Palace of Darkness - Shooter Room: Moon Pearl \ No newline at end of file