From 831ce9ec6aef1edd1dbbddc279f1d5176547adc4 Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Sat, 12 Oct 2024 16:37:22 +0200 Subject: [PATCH 01/25] Fix world copy in HMG/NL --- Main.py | 6 +++++- UnderworldGlitchRules.py | 41 +++++++++++++++++++++++++--------------- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/Main.py b/Main.py index af8ba0ab..bd249668 100644 --- a/Main.py +++ b/Main.py @@ -27,7 +27,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 from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops, fill_specific_items -from UnderworldGlitchRules import create_hybridmajor_connections, create_hybridmajor_connectors +from UnderworldGlitchRules import create_hybridmajor_connections, create_hybridmajor_connectors, get_hybridmajor_connector_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 @@ -557,6 +557,8 @@ 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() + for region in world.regions: copied_region = ret.get_region(region.name, region.player) copied_region.is_light_world = region.is_light_world @@ -566,6 +568,8 @@ def copy_world(world): for location in copied_region.locations: location.parent_region = copied_region for entrance in region.entrances: + if entrance.name in hmg_entrances: + continue ret.get_entrance(entrance.name, entrance.player).connect(copied_region) # fill locations diff --git a/UnderworldGlitchRules.py b/UnderworldGlitchRules.py index 67593631..4544b2ed 100644 --- a/UnderworldGlitchRules.py +++ b/UnderworldGlitchRules.py @@ -6,12 +6,11 @@ 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"), -] +mirehera_spots = [("Mire to Hera Clip", "Mire Torches Top", "Hera Portal")] -icepalace_spots = [("Ice Lobby Clip", "Ice Portal", "Ice Bomb Drop")] +heraswamp_spots = [("Hera to Swamp Clip", "Mire Torches Top", "Swamp Portal")] + +icepalace_spots = [("Ice Lobby Clip", "Ice Portal", "Ice Bomb Drop - Top")] thievesdesert_spots = [ ("Thieves to Desert West Clip", "Thieves Attic", "Desert West Portal"), @@ -32,14 +31,12 @@ paradox_spots = [ kikiskip_connectors = [ ("Kiki Skip Connector", "Spectacle Rock Cave (Bottom)", "Palace of Darkness Exit") ] - - -mireheraswamp_connectors = [ - ("Mire to Hera Connector", "Mire Torches Top", "Tower of Hera Exit"), - ("Mire to Swamp Connector", "Mire Torches Top", "Swamp Palace 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)"), ( @@ -49,7 +46,6 @@ thievesdesert_connectors = [ ), ("Thieves to Desert East Connector", "Thieves Attic", "Desert Palace Exit (East)"), ] - specrock_connectors = [ ( "Spec Rock Top Connector", @@ -68,7 +64,8 @@ specrock_connectors = [ def create_hybridmajor_connections(world, player): for spots in [ kikiskip_spots, - mireheraswamp_spots, + mirehera_spots, + heraswamp_spots, icepalace_spots, thievesdesert_spots, specrock_spots, @@ -81,7 +78,8 @@ def create_hybridmajor_connections(world, player): def create_hybridmajor_connectors(world, player): for connectors in [ kikiskip_connectors, - mireheraswamp_connectors, + mirehera_connectors, + heraswamp_connectors, thievesdesert_connectors, specrock_connectors, ]: @@ -96,6 +94,19 @@ def create_hybridmajor_connectors(world, player): create_no_logic_connections(player, world, new_connectors) +def get_hybridmajor_connector_entrances(): + connectors = [] + for connector in ( + kikiskip_connectors + + mirehera_connectors + + heraswamp_connectors + + thievesdesert_connectors + + specrock_connectors + ): + connectors.append(connector[0]) + return set(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): From b0ca154a92ded90dff2732ead012f91c3a2a6fdf Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Sat, 12 Oct 2024 16:38:27 +0200 Subject: [PATCH 02/25] Actually check if we can leave IP bomb drop --- UnderworldGlitchRules.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/UnderworldGlitchRules.py b/UnderworldGlitchRules.py index 4544b2ed..a03797b0 100644 --- a/UnderworldGlitchRules.py +++ b/UnderworldGlitchRules.py @@ -1,6 +1,8 @@ -from BaseClasses import Entrance +from BaseClasses import Entrance, DoorType +from DoorShuffle import connect_simple_door import Rules from OverworldGlitchRules import create_no_logic_connections +from Doors import create_door kikiskip_spots = [ ("Kiki Skip", "Spectacle Rock Cave (Bottom)", "Palace of Darkness Portal") @@ -73,6 +75,17 @@ def create_hybridmajor_connections(world, player): ]: create_no_logic_connections(player, world, spots) + # 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) + world.doors += [clip_door] + world.initialize_doors([clip_door]) + + ice_bomb_top_reg = world.get_region("Ice Bomb Drop - Top", player) + ice_bomb_top_reg.exits.append( + Entrance(player, "Ice Bomb Drop Clip", ice_bomb_top_reg) + ) + connect_simple_door(world, "Ice Bomb Drop Clip", "Ice Bomb Drop", player) + # Turn dungeons into connectors def create_hybridmajor_connectors(world, player): From dc21885e1baa33568e8871be0bfba836693f3edb Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Sat, 12 Oct 2024 16:42:14 +0200 Subject: [PATCH 03/25] 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 = [ From 7575c3de8ad2a777434ad5de37caeef422b44815 Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Sat, 12 Oct 2024 16:43:09 +0200 Subject: [PATCH 04/25] Tests update --- test/suite/hmg/flippers_wraps.yaml | 2 - .../hmg/inverted_inaccessible_desert.yaml | 1 - test/suite/hmg/inverted_moon_pearl_locs.yaml | 3 ++ test/suite/hmg/moon_pearl_locs.yaml | 4 +- test/suite/hmg/no_east_dw_from_kikiskip.yaml | 49 +++++++++++++++++++ test/suite/hmg/pearlless_sw.yaml | 8 +++ 6 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 test/suite/hmg/no_east_dw_from_kikiskip.yaml diff --git a/test/suite/hmg/flippers_wraps.yaml b/test/suite/hmg/flippers_wraps.yaml index 3418b4ec..9971a38e 100644 --- a/test/suite/hmg/flippers_wraps.yaml +++ b/test/suite/hmg/flippers_wraps.yaml @@ -19,8 +19,6 @@ advanced_placements: locations: Link's House: True Magic Bat: False -advanced_placements: - 1: - type: Verification item: Progressive Glove locations: diff --git a/test/suite/hmg/inverted_inaccessible_desert.yaml b/test/suite/hmg/inverted_inaccessible_desert.yaml index bb8ad8aa..5e59ca63 100644 --- a/test/suite/hmg/inverted_inaccessible_desert.yaml +++ b/test/suite/hmg/inverted_inaccessible_desert.yaml @@ -5,7 +5,6 @@ settings: logic: hybridglitches mode: inverted shuffle: crossed - start_inventory: 1: - Pegasus Boots diff --git a/test/suite/hmg/inverted_moon_pearl_locs.yaml b/test/suite/hmg/inverted_moon_pearl_locs.yaml index 9ae1a9e1..23ec40fe 100644 --- a/test/suite/hmg/inverted_moon_pearl_locs.yaml +++ b/test/suite/hmg/inverted_moon_pearl_locs.yaml @@ -15,6 +15,9 @@ start_inventory: - Ether - Quake - Bombos + - Big Key (Tower of Hera) + - Big Key (Desert Palace) + - Big Key (Eastern Palace) advanced_placements: 1: - type: Verification diff --git a/test/suite/hmg/moon_pearl_locs.yaml b/test/suite/hmg/moon_pearl_locs.yaml index a18d53d1..13a37f92 100644 --- a/test/suite/hmg/moon_pearl_locs.yaml +++ b/test/suite/hmg/moon_pearl_locs.yaml @@ -3,6 +3,7 @@ meta: settings: 1: logic: hybridglitches + shuffle: vanilla start_inventory: 1: - Pegasus Boots @@ -10,9 +11,8 @@ start_inventory: - Fire Rod - Book of Mudora - Progressive Sword - - Progressive Sword - Lamp - - Magic Mirror + - Hammer - Ether - Quake - Bombos diff --git a/test/suite/hmg/no_east_dw_from_kikiskip.yaml b/test/suite/hmg/no_east_dw_from_kikiskip.yaml new file mode 100644 index 00000000..af5ac2ce --- /dev/null +++ b/test/suite/hmg/no_east_dw_from_kikiskip.yaml @@ -0,0 +1,49 @@ +meta: + players: 1 +settings: + 1: + logic: hybridglitches + shuffle: vanilla +start_inventory: + 1: + - Pegasus Boots + - Flippers + - Fire Rod + - Book of Mudora + - Progressive Sword + - Lamp + - Hammer + - Ether + - Quake + - Bombos + - Bombs (10) +placements: + 1: + # Put mirror in PF so we can't DMD + Pyramid Fairy - Right: Magic Mirror + # Lock all swords and cape behind pearl so we can't do aga 1 for pyramid access + Thieves' Town - Big Chest: Progressive Sword + Thieves' Town - Attic: Progressive Sword + Thieves' Town - Blind's Cell: Progressive Sword + Thieves' Town - Map Chest: Cape +advanced_placements: + 1: + - type: Verification + item: Moon Pearl + locations: + 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 + Pyramid Fairy - Left: False + Pyramid: False diff --git a/test/suite/hmg/pearlless_sw.yaml b/test/suite/hmg/pearlless_sw.yaml index c26ce1d9..3b097028 100644 --- a/test/suite/hmg/pearlless_sw.yaml +++ b/test/suite/hmg/pearlless_sw.yaml @@ -9,6 +9,7 @@ start_inventory: - Flippers - Pegasus Boots - Progressive Sword + - Progressive Sword - Hammer - Progressive Glove - Progressive Glove @@ -17,9 +18,16 @@ start_inventory: - Bottle - Magic Mirror - Lamp + - Beat Agahnim 1 + - Cane of Somaria + - Hookshot + - Quake + - Ether + - Bombos advanced_placements: 1: - type: Verification item: Moon Pearl locations: Skull Woods - Bridge Room: True + Skull Woods - Boss: True From 7cecb64ac60936055833579f48d54d641354d9b7 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Fri, 15 Nov 2024 17:37:35 -0600 Subject: [PATCH 05/25] Fixed Blue Potion in shop for Inverted --- Regions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Regions.py b/Regions.py index 6dd82146..1c48c78a 100644 --- a/Regions.py +++ b/Regions.py @@ -1161,7 +1161,7 @@ def create_shops(world, player): world.shops[player] = [] for region_name, (room_id, type, shopkeeper, custom, locked, inventory, sram) in shop_table.items(): if world.mode[player] == 'inverted': - if (not world.is_tile_swapped(0x35, player) and region_name == 'Dark Lake Hylia Shop') \ + if (world.is_tile_swapped(0x35, player) and region_name == 'Dark Lake Hylia Shop') \ or (not world.is_tile_swapped(0x35, player) and region_name == 'Lake Hylia Shop'): locked = True inventory = [('Blue Potion', 160), ('Blue Shield', 50), ('Bombs (10)', 50)] From 15898a4c7026ab9aca12520994ba4b3abb61908d Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 8 Dec 2024 01:19:59 -0600 Subject: [PATCH 06/25] Fixed some OWR compatibility with HMG DM Super Jump --- UnderworldGlitchRules.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/UnderworldGlitchRules.py b/UnderworldGlitchRules.py index e15dad41..6e1c636f 100644 --- a/UnderworldGlitchRules.py +++ b/UnderworldGlitchRules.py @@ -272,16 +272,16 @@ def underworld_glitches_rules(world, player): ) ), } - inverted = world.mode[player] == "inverted" + inverted_dm = (world.mode[player] == "inverted") != world.is_tile_swapped(0x03, player) def hera_rule(state): - return (state.has("Moon Pearl", player) or not inverted) and rule_map.get( + return (state.has("Moon Pearl", player) or not inverted_dm) 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( + return (state.has("Moon Pearl", player) or inverted_dm) and rule_map.get( world.get_entrance(("Ganons Tower"), player).connected_region.name, lambda state: False, )(state) From 9d69745c3792bb4238a06c8f496b85e816555b24 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 8 Dec 2024 01:30:57 -0600 Subject: [PATCH 07/25] Minor casing adjustment --- BaseClasses.py | 4 ++-- source/enemizer/enemy_deny.yaml | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 6abb3d12..2c9cac72 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -3119,9 +3119,9 @@ class Spoiler(object): self.bosses[str(player)] = OrderedDict() self.bosses[str(player)]["Eastern Palace"] = self.world.get_dungeon("Eastern Palace", player).boss.name self.bosses[str(player)]["Desert Palace"] = self.world.get_dungeon("Desert Palace", player).boss.name - self.bosses[str(player)]["Tower Of Hera"] = self.world.get_dungeon("Tower of Hera", player).boss.name + self.bosses[str(player)]["Tower of Hera"] = self.world.get_dungeon("Tower of Hera", player).boss.name self.bosses[str(player)]["Hyrule Castle"] = "Agahnim" - self.bosses[str(player)]["Palace Of Darkness"] = self.world.get_dungeon("Palace of Darkness", player).boss.name + self.bosses[str(player)]["Palace of Darkness"] = self.world.get_dungeon("Palace of Darkness", player).boss.name self.bosses[str(player)]["Swamp Palace"] = self.world.get_dungeon("Swamp Palace", player).boss.name self.bosses[str(player)]["Skull Woods"] = self.world.get_dungeon("Skull Woods", player).boss.name self.bosses[str(player)]["Thieves Town"] = self.world.get_dungeon("Thieves Town", player).boss.name diff --git a/source/enemizer/enemy_deny.yaml b/source/enemizer/enemy_deny.yaml index 301abda2..bf916dc3 100644 --- a/source/enemizer/enemy_deny.yaml +++ b/source/enemizer/enemy_deny.yaml @@ -21,7 +21,7 @@ UwGeneralDeny: - [ 0x0016, 1, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "GreenMimic", "RedMimic", "Pikit" ] ] #"Swamp Palace - Pool - Zol 2" - [ 0x0016, 2, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "GreenMimic", "RedMimic", "Pikit" ] ] #"Swamp Palace - Pool - Blue Bari" - [ 0x0016, 3, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - Pool - Zol 3" - - [ 0x0017, 5, [ "Beamos", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Tower Of Hera - Bumper Room - Fire Bar (Clockwise)" + - [ 0x0017, 5, [ "Beamos", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Tower of Hera - Bumper Room - Fire Bar (Clockwise)" - [ 0x0019, 0, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Palace of Darkness - Dark Maze - Kodongo 1" - [ 0x0019, 1, [ "RollerVerticalUp" ] ] #"Palace of Darkness - Dark Maze - Kodongo 2" - [ 0x0019, 2, [ "RollerVerticalDown", "RollerVerticalUp" ] ] #"Palace of Darkness - Dark Maze - Kodongo 3" @@ -46,11 +46,11 @@ UwGeneralDeny: - [ 0x0026, 9, [ "RollerHorizontalRight", "Statue" ] ] #"Swamp Palace - Big Spoon - Kyameron" - [ 0x0026, 10, [ "Statue" ] ] # multiple push statues in this room can cause issues - [ 0x0026, 11, [ "Statue" ] ] # multiple push statues in this room can cause issues - - [ 0x0027, 0, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalLeft", "FirebarCW" ] ] #"Tower Of Hera - Petting Zoo - Mini Moldorm 1" - - [ 0x0027, 1, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Tower Of Hera - Petting Zoo - Mini Moldorm 2" - - [ 0x0027, 2, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Tower Of Hera - Petting Zoo - Mini Moldorm 3" + - [ 0x0027, 0, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalLeft", "FirebarCW" ] ] #"Tower of Hera - Petting Zoo - Mini Moldorm 1" + - [ 0x0027, 1, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Tower of Hera - Petting Zoo - Mini Moldorm 2" + - [ 0x0027, 2, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Tower of Hera - Petting Zoo - Mini Moldorm 3" - [ 0x0027, 4, ["Bumper", "BigSpike", "AntiFairyCircle", "RollerVerticalDown", "RollerVerticalUp"]] - - [ 0x0027, 5, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Tower Of Hera - Petting Zoo - Kodongo 1" + - [ 0x0027, 5, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Tower of Hera - Petting Zoo - Kodongo 1" - [0x0028, 0, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]] - [0x0028, 1, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]] - [0x0028, 2, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]] From d95e5cf4d27cdee22f549ee9053903657c8fe1e8 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 10 Dec 2024 22:25:40 -0600 Subject: [PATCH 08/25] Fixed issue with plandoing prizes --- ItemList.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ItemList.py b/ItemList.py index 5a6f33e7..6c9bf589 100644 --- a/ItemList.py +++ b/ItemList.py @@ -1669,16 +1669,16 @@ def get_item_and_event_flag(item, world, player, dungeon_pool, prize_set, prize_ item_player = player if len(item_parts) < 2 else int(item_parts[1]) item_name = item_parts[0] event_flag = False - if is_dungeon_item(item_name, world, item_player): - item_to_place = next(x for x in dungeon_pool - if x.name == item_name and x.player == item_player) - dungeon_pool.remove(item_to_place) - event_flag = True - elif item_name in prize_set: + if item_name in prize_set: item_player = player # prizes must be for that player item_to_place = ItemFactory(item_name, item_player) prize_pool.remove(item_name) event_flag = True + elif is_dungeon_item(item_name, world, item_player): + item_to_place = next(x for x in dungeon_pool + if x.name == item_name and x.player == item_player) + dungeon_pool.remove(item_to_place) + event_flag = True else: matcher = lambda x: x.name == item_name and x.player == item_player if item_name == 'Bottle': From 0d5872ef74423ac7ab54cfc7ba13ad1339ad167d Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Thu, 12 Dec 2024 20:44:26 +0100 Subject: [PATCH 09/25] Don't ever check playthough when all seeds are no logic --- Main.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Main.py b/Main.py index bd249668..4d806e96 100644 --- a/Main.py +++ b/Main.py @@ -426,6 +426,10 @@ def main(args, seed=None, fish=None): if 'debug' in world.spoiler.settings: world.spoiler.extras(output_path(f'{outfilebase}_Spoiler.txt')) + player_logics = set(world.logic.values()) + if len(player_logics) == 1 and 'nologic' in player_logics: + args.skip_playthrough = True + if not args.skip_playthrough: logger.info(world.fish.translate("cli","cli","calc.playthrough")) create_playthrough(world) From 8b73fee1f235b1edc0e0ebbd99af4072493651b0 Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Thu, 12 Dec 2024 20:56:59 +0100 Subject: [PATCH 10/25] SP Bosstest for MP --- test/suite/hmg/moon_pearl_locs.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/test/suite/hmg/moon_pearl_locs.yaml b/test/suite/hmg/moon_pearl_locs.yaml index 13a37f92..b2d418b1 100644 --- a/test/suite/hmg/moon_pearl_locs.yaml +++ b/test/suite/hmg/moon_pearl_locs.yaml @@ -41,6 +41,7 @@ advanced_placements: C-Shaped House: True Pyramid Fairy - Left: True Swamp Palace - Entrance: False + Swamp Palace - Boss: False Thieves' Town - Map Chest: False From 459eca9b4a980d3089c15b17abf4a7a4c0dbdbd4 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Thu, 12 Dec 2024 18:26:31 -0600 Subject: [PATCH 11/25] Fixed issue with District ER not including SW as a connector --- source/overworld/EntranceShuffle2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index 340430ac..ff7171d2 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -907,7 +907,7 @@ def figure_out_connectors(exits, avail, cross_world=True): cave_list = list(Connector_List) if avail.assumed_loose_caves or (not avail.skull_handled and (cross_world or not avail.world.is_tile_swapped(0x00, avail.player))): skull_connector = [x for x in ['Skull Woods Second Section Exit (West)', 'Skull Woods Second Section Exit (East)'] if x in exits] - cave_list.extend(skull_connector) + cave_list.extend([skull_connector]) if avail.assumed_loose_caves or not avail.keep_drops_together: cave_list.extend([[entrance_map[e]] for e in linked_drop_map.values() if 'Inverted ' not in e and 'Skull Woods ' not in e]) From 385165f7c7ea0b59427c01896742deb3d0a2ffcc Mon Sep 17 00:00:00 2001 From: codemann8 Date: Fri, 13 Dec 2024 00:52:27 -0600 Subject: [PATCH 12/25] Fixed issue with HMG/NL --- Rules.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Rules.py b/Rules.py index 6a7296ab..896b3c12 100644 --- a/Rules.py +++ b/Rules.py @@ -98,7 +98,8 @@ def set_rules(world, player): # These rules go here because they overwrite/add to some of the above rules if world.logic[player] == 'hybridglitches': - underworld_glitches_rules(world, player) + if not world.is_copied_world: + 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. From 7e952c0b9e142e702c8049eab3f1decd03f73753 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sat, 14 Dec 2024 16:17:52 -0600 Subject: [PATCH 13/25] Base ROM update - Improved psuedobots / music fixes --- Rom.py | 2 +- data/base2current.bps | Bin 133928 -> 133996 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index aa2ca374..a19590e8 100644 --- a/Rom.py +++ b/Rom.py @@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '2d658871687c966f35dd9597a4195256' +RANDOMIZERBASEHASH = 'a51679f0e31ee29f59247545d705992a' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index 617699b79b4c422c17c714b8313f0323376f6095..680045bc2d2f2419af0062d94da0c5ca14e74799 100644 GIT binary patch delta 6593 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 UP2SchlVN>=c`s&8?)BdP2NBgIZU6uP delta 6431 zcmW+b30xCb^E;b^a3=_epb{2DKm-)LvEsp5u;6{5;!#D>7VjH&BM|~5A*}GghzVpP z1jK+*f=9tWv_jNsJgcZ+Y3l)2sa4zheV_c7?r-OKv+vEmnSF2GO!B%^P*W->8q4i? zN!?M68v|adlvIAx9ftVbsxnOg+s5A<0IKMB@YJ;^k0DyLk{U{Kf+CA3@`Rk&PXB_> zM(_48J_=9|tJ^vsM?9D48(`|_AfDaE}kCz7q&D=H$Xth#UH|CJC8w9BJT)ObeiH7UI6jkLW2#9 zi1KcxudhZ*oTkN<$5g~Jwi!v9&d7bc2#K=sw5;_#?dI->w|3Jr+(*D>(_(iI95$N1 z@hArTZJr7DTm^8{l;-8lbsOKsxRO+bmZ~VDD(+G8C{j)cOvk)ATqvi@e8<6Q`g>p6 zmLO#}pD=~W^`?H+tk{PC82)2#pnK&@$Y(0jnGCryijR|1jzoOQK|Xe70Lrggs;VGPDdl-6I_!s!wUb-xNIaJW^!Y9~2$zzJB z!d>iiQ|XWhHU4KleLX@0lS~EMd4SMv-M` zeWEH;A}1>812g0S?YEg*jsoSC`WsjRT!SF27+j!PMT{wA`YxBu5x#uc*Edi?)YGAn zBZfmc)A6VUX92F5jYv%=r zbayjll%$O2qmd*f&&(+&ggs1RNBcuc5=F_0jXjK3POR!-QrX7;y6I2zp1Rn+V1y*4 zxIrm|R7GK181XIrDtaT{_<>G|3C5d0(AhCw4TM~2eFp`yy)Wi$*|UBiSG*$aF^x| z&_TAe>C(Pc9Co3PIK;Ggj$7WD{@d__D}KF) zZvSBeOs4~`mV&_4di5fQi((3B`tY?Je45gPUsnMhyv9^=Lu?B;-E@CC5T-xw9~U~% zW48%Nsa$wG>B|PXQ_72LJbTwCOd7FZ2o;(9f3E`& zn(n+=!g0F$n8{!qre;{SSh<9Rp8W2t7MA4p1kUHD1CqxJv^w41O%qi!2-~&%l?uX2f-| z0|wO$;f9!A;ivQrLjFig_}*ZM&|}pEMz#{#9ELcIqTRsXfylUAlMzR}mC#}d+T#Wj z`REr$J$yJi;|AXD0xyLOAK)YJlpNYGB}WDWW06k^`o#^Z@ZYT{*BwG(7c#p;pwQ#s zkrLugj@Fo~&W@MMJC= zWr4AflZn0_mUF>sU$v)L`|RJ26O4sut4of|zUmu|J4ubA>nT#%Ky73v!j3v$Sx}W# zto>DJoWsc!-$DOSLi(YM*1pzcAEjH=Uy)RR@}Yb+t$nG^Lw~FF6h}FUR6Srvh!CGd z-Rd5StGukbS*%TULHUR&*hZEs*oMd$O)Dy@N{h8=B4Z5NN0y%~)@~3PW09i#RIxT) zWQ=3EGsW7iB4a$uHL-Syj0r5)!cJUd{1Rm=%C8n{Uy6)LtgyRS`&wk2&vMUMjV{Jy zmV3i0bTOu|+(%ZSi*W(kFQFD9A4(>)fbx@^Piwc*E2v7xs?Y3eT#O58=7RQ}(3r{^ z`m{j1Lu6cqc2V+oBI9bcT;^h2Lm!nImm|kyMI-AgdX4sPp}4YuoIPDPIawiRT^_E? zqxP_F8>5s^Su7pS(xXFe)V-1#S7<6#3R=5Qq|B$A(F4s-RH^2n%(m*JS|v5E)O;sD zMQi`AW~eio%dE6XtwLU2FafGjk{68R-+slYH*Z4Uvi#c7wD!M36!Vp$+VEQ6zf&%l){n-vK#-+%aqIPM*NY!Lo zJ8~GZl~5t-=?Yq3i4QHIFVJf*2y_41H%GIp#z?pWK%g&1Lj;UOJ4eH8!Qt3nsejZiW6|Z&Q01MJQuuU@pf{{*1rd3VIq)f9 zXba3Yd^1X}7>2|P(P3|x0S0uz8-8_)&6ZFTf048bvnA^IEL10kaM7G>b}LxvHWew7WD~@hb8Mm#R-ikOEf1m%UDv0*80IPhczeA7poM? zq-!XAE+YM4v~%tFy{~1T($h|6EojuZc%svOuoRD;gLq@$>lu4awuMziaZBgzzF<&S zUFI65I}xYb)JEY1xvVOZ*Iuq_;Tej!nq#F~&q@=|DStm{Yf|ssT3PMB4e7^#KfY!( z`f&`*Np+Ymr?z&PJJx<*yWwCJwNJgrDW4)uN~)|~eTpZiyil}L{_!vY@K zx9Wm-UDIt&xL$D@!xpo!>MVati3(${{p#^+BoxIeDQ_P=?pBv~+PV7rtMx;VZnF3R zjg#%O@%1;&p93HfxbuB}TZ`rg!9*T55*vjfX%PGlPH5*i2(XRZSJB9tq2-#Haj+Wq z-aIGwhsoGv5^^Ijj{uB38u%;r8>U&XAeWZXI~SOJLcksH)H!HcC~Ss6qzi>@phu!G zILXF|hA>DUnKVm&n}Zo5up*AZ1y{Dr$Vzi2o$<5dko!y&F#%$;H>~|pB{#_ z+r7>y_Rb(Rvwdz-jdiQjuSgpw*6o{8D8sN$(^0D1B&qLf#IPNvA?tF^X%+|fQ>S%% zSbRe*(N8^(VVNxKJKs9Xql-GxKF_0zbrEy^oJl8bZR-9en3UabdP?6>{nmtGzh5tq zerQ7Ops%neT_g?SbGkuNnlKXy^(F6&Xw*d5@47$cH-jJeK@>%jLH_UPI+PHLYA3=* zIEO^xFj{m@hfDs(FNY2eI*h%i<2U1BkuzgFzArQ`)uY|)+ zoNY!-hIO`a=K+$(*pcZ-GZ`Ys-Z)= z5f`deA9xon%?~a{U9vQ9zdYwVOS6uJT`keAmS$t?h$|g>Wubg&TR^)~MitZV@XZ9c|+J zb$^OK-_mny70DfXUaew5hn`=nSk$4nsZ}iL=oQnWC6uR<4Zoh~RWvB^XSdLS81VDj zbF(+B-hpfJY7hl}u1%=vS|NUQvsZZFleijPh=EzQ`+Z;h#%}SuHxa~wyUPmS7xWTJ zYMh3g17A?@)lvQ^Fcze^8ojbyb8u^q%(i|5$_hXp zaWH=9&9QOt&7x!U7Zwijz`R$~f}J-Y~mb)>gm%)-8QZ0(;pO)hM~-qd;fd zqS`cqJuchAjNIgG+oB>V*T_d_FYpbRU#~cgbwryuL;M-p#zVe?o8Qs8Z+wqBlHR{r zktQCJqt?dAP+E+PioPT<%*!0GIjH=ZlSw+EetDD@!<2c~Cpq;TKOEbr+({PaHCADm zM>fXGeEIU(XJ>`9I?fq|CcyioVTZaeJh{WXzrzf>%edZSg6=V9uTmMC*p?nTcRoWL zet$#f2FuWqXtk$3j+D5GGp`B%sr4>3=vDXq;UW4a38u_Ip#j?SZ|*Z5n{(3o zmADAgTS^`FT&j0^z>0Kh`;{&ttaMu3CEFv)(ITwhV>1WqSNi|Up{^v@$w~LALi6WC z$Ta!{bBa^J7oNgeOXveq)=&wBXbI)S(sEj&7YZ+33&Y8ayMW2g6yaSKDQxkQBI!-|R-51whX|oasnD;->yMAY~6UgH1)U zv-r&u;>G)u;*h7BC#k^jwAIB?QtZC;akVI{o%&n1z~iyPWgDrU;fWqC zU=tpR*=Zqg0Uw=&To*xnc0o@O-_z4G@bjRwvzJ|C<9cdx!o;IkJRzei-(#~FtJPxS z@jai?wEPRbSdXAx;g7Y(wJU`9b8O>K#Ktf&tHdR6a8PI;KRD=VXE%h??CjE5H6CC0 zN_&U2dVKv-`Z7cB*(;>y9jOS!ur^^vAV-OwEP`k#Ld0Uo9<%%xral-ff&r1yQ_^4+ z7!S5&Sw6GdE#>DkxcJ$^@dKPh4Z6A*W-JK$ncdY19-GILyVJ=XXu^J#+9vfwx%ExD zA(C8;urL3XTYo11&}2C&(eV;%x?v_6r8dah$v@&ye3t6M04JS%kWQW-iVXs*FWTdS z7A^rAg3ya4FngM4yMQcR_*vQ}>`4ArKbTBe%^W zaxulm53HfOS7@=pQ2J6x#9h8dO-msJKXe2=UkYD1u%#LuEt4H;6RQ(Y*fNkiT<$4a z)Fw7&x1dSGk!2Z#1=K{fgbGqiWJIqRjmxJRXg}1w0`@x8zF=xv zYhEx?PU|VOb|rWTPYy{=&;ki{{JMnR^c5;x$tD}o=+R2>hm_J(&RVjI%Dbs;*N3JF1+Wj7zSg>}VQUUC{jSbwttv{xMHth7na)@1Jdv0TYd&h8I% zM%%tc{;OaNZ*t&K%@UNn3P#}Tm!XVRkQ`zc``KqjBc76{{e>`-0W zxXT>71=?^tWC^n02FE>xhjJ@9`ZpP^L_3v%#vaO5Ii*`2;?x^$&8^!Yi-Y?wN3*uW zXgoC9yk?pIe89oDP>n^~lJbRrW@V^@UCjUN zhE6UXF$=XHfQfjj7y9b}1c^o*$kSgbBL@=l6Kah+Q1k}##DieQ@jW=|Kg?RYcBPr0 z1y_I*oP7&fvf22)Kg5}oEyh-s2FDi^@9(76sS`qxEeXM}6tSsh2<|l*Z6x7!VA#s% z6pp@K-d)18!33Cn>E^ixy`z&ehKaCYu5KEiNj206UD`M^I!Hm*$fl5_0_m1*=$sV8 z(A<z@)R}fDI~OM4dwk7jb72J!57~>HsBgNapWj=zibIHN0h^G+}STqS@G#*+NU%r zPf#7P(7ZwoS_q%I;b@6JhOs{oot*EKQ(aOTvGjhG_a*}p5Xa=eMsei?Qhg8Irikhz zb>Tk8=pX`(uYfc>)DI~uAO`39qn|2Zo5S9*$5aWdqSEF}4_mUNu+}+0zAs=o`%97mv-%!Gk zC+EyHG|9Ph*&3TS*C2Ez5A4m6re}Oha;KSP|ADnOb_*Bh6+5y2Kcx7Y-{bg7o@g8Y hd;JXTFqVczu~n35f2ui6fO9#5Q=M~NntLzh{6DSv--rMJ From 22c93594597b96a776069b4cd529b1508fbed9a7 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sat, 14 Dec 2024 16:20:42 -0600 Subject: [PATCH 14/25] Minor district error --- source/item/District.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/item/District.py b/source/item/District.py index baf46a82..1ad4fba6 100644 --- a/source/item/District.py +++ b/source/item/District.py @@ -111,7 +111,7 @@ def resolve_districts(world): visited = set() while len(queue) > 0: region = queue.pop() - if not region: + if region is None: RuntimeError(f'No region connected to entrance: {ent.name} Likely a missing entry in OWExitTypes') visited.add(region) if region.type == RegionType.Cave: From 0ead417d99f17ef823eef6038a3b2348fb9747eb Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sat, 14 Dec 2024 16:47:57 -0600 Subject: [PATCH 15/25] Fix world copy in HMG/NL --- Main.py | 5 ++++- UnderworldGlitchRules.py | 39 ++++++++++++++++++++++++++------------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/Main.py b/Main.py index c7aecaac..754966c6 100644 --- a/Main.py +++ b/Main.py @@ -28,7 +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 UnderworldGlitchRules import create_hybridmajor_connections, create_hybridmajor_connectors, get_hybridmajor_connector_entrances from Utils import output_path, parse_player_names from source.item.District import init_districts @@ -656,6 +656,7 @@ 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() for region in world.regions: copied_region = ret.get_region(region.name, region.player) copied_region.is_light_world = region.is_light_world @@ -665,6 +666,8 @@ def copy_world(world): for location in copied_region.locations: location.parent_region = copied_region for entrance in region.entrances: + if entrance.name in hmg_entrances: + continue ret.get_entrance(entrance.name, entrance.player).connect(copied_region) for exit in region.exits: if exit.connected_region: diff --git a/UnderworldGlitchRules.py b/UnderworldGlitchRules.py index 6e1c636f..c645b719 100644 --- a/UnderworldGlitchRules.py +++ b/UnderworldGlitchRules.py @@ -6,12 +6,11 @@ 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"), -] +mirehera_spots = [("Mire to Hera Clip", "Mire Torches Top", "Hera Portal")] -icepalace_spots = [("Ice Lobby Clip", "Ice Portal", "Ice Bomb Drop")] +heraswamp_spots = [("Hera to Swamp Clip", "Mire Torches Top", "Swamp Portal")] + +icepalace_spots = [("Ice Lobby Clip", "Ice Portal", "Ice Bomb Drop - Top")] thievesdesert_spots = [ ("Thieves to Desert West Clip", "Thieves Attic", "Desert West Portal"), @@ -34,12 +33,12 @@ kikiskip_connectors = [ ] -mireheraswamp_connectors = [ - ("Mire to Hera Connector", "Mire Torches Top", "Tower of Hera Exit"), - ("Mire to Swamp Connector", "Mire Torches Top", "Swamp Palace 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)"), ( @@ -49,7 +48,6 @@ thievesdesert_connectors = [ ), ("Thieves to Desert East Connector", "Thieves Attic", "Desert Palace Exit (East)"), ] - specrock_connectors = [ ( "Spec Rock Top Connector", @@ -68,7 +66,8 @@ specrock_connectors = [ def create_hybridmajor_connections(world, player): for spots in [ kikiskip_spots, - mireheraswamp_spots, + mirehera_spots, + heraswamp_spots, icepalace_spots, thievesdesert_spots, specrock_spots, @@ -81,7 +80,8 @@ def create_hybridmajor_connections(world, player): def create_hybridmajor_connectors(world, player): for connectors in [ kikiskip_connectors, - mireheraswamp_connectors, + mirehera_connectors, + heraswamp_connectors, thievesdesert_connectors, specrock_connectors, ]: @@ -97,6 +97,19 @@ def create_hybridmajor_connectors(world, player): create_no_logic_connections(player, world, new_connectors) +def get_hybridmajor_connector_entrances(): + connectors = [] + for connector in ( + kikiskip_connectors + + mirehera_connectors + + heraswamp_connectors + + thievesdesert_connectors + + specrock_connectors + ): + connectors.append(connector[0]) + return set(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): From 023bbede0a6c720fd0f80981773a06a785489fa2 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 15 Dec 2024 14:50:10 -0600 Subject: [PATCH 16/25] Skip playthrough calc when all seeds are NL --- Main.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Main.py b/Main.py index 754966c6..88a3c483 100644 --- a/Main.py +++ b/Main.py @@ -372,6 +372,10 @@ def main(args, seed=None, fish=None): if 'debug' in world.spoiler.settings: world.spoiler.extras(output_path(f'{outfilebase}_Spoiler.txt')) + player_logics = set(world.logic.values()) + if len(player_logics) == 1 and 'nologic' in player_logics: + args.skip_playthrough = True + if not args.skip_playthrough: logger.info(world.fish.translate("cli","cli","calc.playthrough")) create_playthrough(world) From af1892c7d51e485591de34bc583e6489ef2ce71c Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Mon, 16 Dec 2024 00:06:54 +0100 Subject: [PATCH 17/25] 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 4d806e96..fcb50142 100644 --- a/Main.py +++ b/Main.py @@ -27,7 +27,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 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 97883dc6..55e9d896 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 768588d3..303aeed2 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 e82dd74b..6cb44847 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 0f4389df..beb95ed9 100644 --- a/UnderworldGlitchRules.py +++ b/UnderworldGlitchRules.py @@ -29,42 +29,10 @@ 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 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 underworld_glitches_rules(world, 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 underworld_glitches_rules(world, 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 underworld_glitches_rules(world, player): 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 5e59ca63..2a55777d 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 00000000..19de4b63 --- /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 From 1075bd821c1089f17b0038be5cf2917679cb840c Mon Sep 17 00:00:00 2001 From: codemann8 Date: Mon, 16 Dec 2024 08:20:07 -0600 Subject: [PATCH 18/25] Fixed issue with Good Bee Cave getting chosen as a Take Any Cave --- ItemList.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ItemList.py b/ItemList.py index 6c9bf589..35ff2d0a 100644 --- a/ItemList.py +++ b/ItemList.py @@ -520,8 +520,8 @@ def set_up_take_anys(world, player, skip_adjustments=False): world.regions.append(old_man_take_any) world.dynamic_regions.append(old_man_take_any) - reg = regions.pop() - entrance = world.get_region(reg, player).entrances[0] + reg = world.get_region(regions.pop(), player) + entrance = next((e for e in reg.entrances if e.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld])) connect_entrance(world, entrance, old_man_take_any, player) entrance.target = 0x58 old_man_take_any.shop = Shop(old_man_take_any, 0x0112, ShopType.TakeAny, 0xE2, True, not world.shopsanity[player], 32) From db8fbb448d575e829ea439f7578f860c5e94d5d5 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Mon, 16 Dec 2024 09:23:07 -0600 Subject: [PATCH 19/25] Fixed Insanity ER to exit correctly out of GT entrance --- source/overworld/EntranceShuffle2.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index ff7171d2..f032a616 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -1917,8 +1917,8 @@ def connect_exit(exit_name, entrancename, avail): exit.connected_region.entrances.remove(exit) dest_region = entrance.parent_region - if dest_region.name == 'Pyramid Crack': - # Needs to logically exit into greater Pyramid Area + if dest_region.name in ['Pyramid Crack', 'GT Stairs']: + # Needs to logically exit into greater OW area dest_region = entrance.parent_region.entrances[0].parent_region exit.connect(dest_region, door_addresses[entrance.name][1], exit_ids[exit.name][1]) From a5bfd41aca9dc6974492d40f81d1b3742e3d2f4d Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 17 Dec 2024 05:27:46 -0600 Subject: [PATCH 20/25] Added Thieves Maiden to followers that have pathing logic --- BaseClasses.py | 7 ++++--- DoorShuffle.py | 1 + Dungeons.py | 3 ++- Regions.py | 3 ++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 2c9cac72..aa9729ba 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1658,14 +1658,15 @@ class Entrance(object): 'Missing Smith': ('Frog', True, False, True, True), 'Middle Aged Man': ('Dark Blacksmith Ruins', True, False, True, True), 'Old Man Drop Off': ('Lost Old Man', True, False, False, False), - #'Revealing Light': ('Suspicious Maiden', False, False, False, False) } + 'Revealing Light': ('Suspicious Maiden', False, False, False, False) } if self.name in multi_step_locations: if self not in state.path: world = self.parent_region.world - step_location = world.get_location(multi_step_locations[self.name][0], self.player) - if step_location.can_reach(state) and self.can_reach_thru(state, step_location, multi_step_locations[self.name][1], multi_step_locations[self.name][2], multi_step_locations[self.name][3], multi_step_locations[self.name][4]) and self.access_rule(state): + multi_step_loc = multi_step_locations[self.name] + step_location = world.get_location(multi_step_loc[0], self.player) + if step_location.can_reach(state) and self.can_reach_thru(state, step_location, multi_step_loc[1], multi_step_loc[2], multi_step_loc[3], multi_step_loc[4]) and self.access_rule(state): if not self in state.path: path = state.path.get(step_location.parent_region, (step_location.parent_region.name, None)) item_name = step_location.item.name if step_location.item else 'Pick Up Item' diff --git a/DoorShuffle.py b/DoorShuffle.py index b5a1e1dd..1f2fed26 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -3792,6 +3792,7 @@ logical_connections = [ ('Thieves Conveyor Block Path', 'Thieves Conveyor Bridge'), ("Thieves Blind's Cell Door", "Thieves Blind's Cell Interior"), ("Thieves Blind's Cell Exit", "Thieves Blind's Cell"), + ('Revealing Light', 'Thieves Revealing Light'), ('Thieves Town Boss', 'Thieves Boss Spoils'), ('Ice Cross Bottom Push Block Left', 'Ice Floor Switch'), diff --git a/Dungeons.py b/Dungeons.py index 8d72760b..f7cb4f80 100644 --- a/Dungeons.py +++ b/Dungeons.py @@ -139,7 +139,8 @@ thieves_regions = [ 'Thieves Attic', 'Thieves Attic Hint', 'Thieves Attic Switch', 'Thieves Cricket Hall Left', 'Thieves Cricket Hall Right', 'Thieves Attic Window', 'Thieves Basement Block', 'Thieves Blocked Entry', 'Thieves Lonely Zazak', "Thieves Blind's Cell", "Thieves Blind's Cell Interior", 'Thieves Conveyor Bridge', - 'Thieves Conveyor Block', 'Thieves Big Chest Room', 'Thieves Trap', 'Thieves Boss Spoils', 'Thieves Town Portal' + 'Thieves Conveyor Block', 'Thieves Big Chest Room', 'Thieves Trap', 'Thieves Revealing Light', + 'Thieves Boss Spoils', 'Thieves Town Portal' ] ice_regions = [ diff --git a/Regions.py b/Regions.py index 1c48c78a..fa25fa30 100644 --- a/Regions.py +++ b/Regions.py @@ -718,7 +718,8 @@ def create_dungeon_regions(world, player): create_dungeon_region(player, 'Thieves Compass Room', 'Thieves\' Town', ['Thieves\' Town - Compass Chest'], ['Thieves Compass Room NW Edge', 'Thieves Compass Room N Edge', 'Thieves Compass Room WS Edge', 'Thieves Compass Room W']), create_dungeon_region(player, 'Thieves Big Chest Nook', 'Thieves\' Town', ['Thieves\' Town - Big Key Chest'], ['Thieves Big Chest Nook ES Edge']), create_dungeon_region(player, 'Thieves Hallway', 'Thieves\' Town', ['Thieves\' Town - Hallway Pot Key'], ['Thieves Hallway SE', 'Thieves Hallway NE', 'Thieves Hallway WN', 'Thieves Hallway WS']), - create_dungeon_region(player, 'Thieves Boss', 'Thieves\' Town', ['Revealing Light'], ['Thieves Boss SE', 'Thieves Town Boss']), + create_dungeon_region(player, 'Thieves Boss', 'Thieves\' Town', None, ['Thieves Boss SE', 'Revealing Light', 'Thieves Town Boss']), + create_dungeon_region(player, 'Thieves Revealing Light', 'Thieves\' Town', ['Revealing Light']), create_dungeon_region(player, 'Thieves Boss Spoils', 'Thieves\' Town', ['Thieves\' Town - Boss', 'Thieves\' Town - Prize', 'Thieves\' Town - Boss Kill']), create_dungeon_region(player, 'Thieves Pot Alcove Mid', 'Thieves\' Town', None, ['Thieves Pot Alcove Mid ES', 'Thieves Pot Alcove Mid WS']), create_dungeon_region(player, 'Thieves Pot Alcove Bottom', 'Thieves\' Town', None, ['Thieves Pot Alcove Bottom SW']), From c38cb4d8747aa07a900ccd877e9573c09ce08618 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 17 Dec 2024 05:32:07 -0600 Subject: [PATCH 21/25] Some corrections and improvements to pathing logic --- BaseClasses.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index aa9729ba..9289fa34 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1730,7 +1730,8 @@ class Entrance(object): # this is checked first as this often the shortest path follower_region = start_region if follower_region.type not in [RegionType.LightWorld, RegionType.DarkWorld]: - follower_region = [i for i in start_region.entrances if i.parent_region.name != 'Menu'][0].parent_region + ent_list = [e for e in start_region.entrances if e.parent_region.type != RegionType.Menu] + follower_region = ent_list[0].parent_region if (follower_region.world.mode[self.player] != 'inverted') == (follower_region.type == RegionType.LightWorld): from OverworldShuffle import get_mirror_edges mirror_map = get_mirror_edges(follower_region.world, follower_region, self.player) @@ -1748,7 +1749,8 @@ class Entrance(object): path = (follower_region.name, (mirror_exit, path)) item_name = step_location.item.name if step_location.item else 'Pick Up Item' if start_region.name != follower_region.name: - path = (start_region.name, (start_region.entrances[0].name, path)) + ent_list = [e for e in start_region.entrances if e.parent_region.type != RegionType.Menu] + path = (start_region.name, (ent_list[0].name, path)) path = (f'{step_location.parent_region.name} Exit', ('Leave Item Area', (item_name, path))) else: path = (item_name, path) @@ -1759,25 +1761,36 @@ class Entrance(object): path = (self.parent_region.name, path) state.path[self] = (self.name, path) - if not found: - # check normal paths - traverse_paths(start_region.entrances[0].parent_region, self.parent_region) + for ent in start_region.entrances: + if not found: + # check normal paths + if ent.parent_region.type != RegionType.Menu and (ent.parent_region.type == start_region.type or \ + ent.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld] or not ignore_underworld): + traverse_paths(ent.parent_region, self.parent_region) if not found and allow_save_quit: # check paths involving save and quit exit = self.parent_region.world.get_entrance('Links House S&Q', self.player) traverse_paths(exit.connected_region, self.parent_region, [exit]) + if not found: + world = self.parent_region.world + exit = world.get_entrance('Sanctuary S&Q', self.player) + # only allow save and quit at Sanc if the lobby is guaranteed vanilla + if exit.connected_region != 'Sanctuary' or world.mode[self.player] == 'standard' \ + or world.doorShuffle[self.player] == 'vanilla' or world.intensity[self.player] < 3: + traverse_paths(exit.connected_region, self.parent_region, [exit]) if not found and allow_mirror_reentry and state.has_Mirror(self.player): # check for paths using mirror portal re-entry at location of final destination # this is checked last as this is the most complicated/exhaustive check follower_region = start_region if follower_region.type not in [RegionType.LightWorld, RegionType.DarkWorld]: - follower_region = start_region.entrances[0].parent_region + ent_list = [e for e in start_region.entrances if e.parent_region.type != RegionType.Menu] + follower_region = ent_list[0].parent_region if (follower_region.world.mode[self.player] != 'inverted') == (follower_region.type == RegionType.LightWorld): dest_region = self.parent_region if dest_region.type not in [RegionType.LightWorld, RegionType.DarkWorld]: - dest_region = start_region.entrances[0].parent_region + dest_region = dest_region.entrances[0].parent_region if (dest_region.world.mode[self.player] != 'inverted') != (dest_region.type == RegionType.LightWorld): # loop thru potential places to leave a mirror portal from OverworldShuffle import get_mirror_edges From 60c1334c27baa90d926420f338358d96dd0f5500 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 17 Dec 2024 06:24:20 -0600 Subject: [PATCH 22/25] Minor fix in ER to remove assumption there is only one way into a cave --- source/overworld/EntranceShuffle2.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index f032a616..994f5f50 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -1631,7 +1631,8 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit): cave_entrances = [] for cave_exit in rnd_cave[:-1]: if avail.swapped and cave_exit not in avail.exits: - entrance = avail.world.get_entrance(cave_exit, avail.player).parent_region.entrances[0].name + entrance = avail.world.get_entrance(cave_exit, avail.player) + entrance = next((e for e in entrance.parent_region.entrances if e.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld])).name cave_entrances.append(entrance) else: entrance = next(e for e in entrances[::-1] if e not in invalid_connections[exit] and e not in must_exit From a5d572a41c9e84c0e1bf5c1f6740eac1796aa2c5 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 17 Dec 2024 06:26:07 -0600 Subject: [PATCH 23/25] Prevent duplicate entrance entries --- BaseClasses.py | 3 ++- Main.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 9289fa34..e74c9ac5 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1843,7 +1843,8 @@ class Entrance(object): self.target = target self.addresses = addresses self.vanilla = vanilla - region.entrances.append(self) + if self not in region.entrances: + region.entrances.append(self) def __str__(self): return str(self.__unicode__()) diff --git a/Main.py b/Main.py index 75c3a211..b5250228 100644 --- a/Main.py +++ b/Main.py @@ -863,7 +863,9 @@ def copy_world_premature(world, player): for location in copied_region.locations: location.parent_region = copied_region for entrance in region.entrances: - copied_region.entrances.append(ret.get_entrance(entrance.name, entrance.player)) + ent = ret.get_entrance(entrance.name, entrance.player) + if ent not in copied_region.entrances: + copied_region.entrances.append(ent) for exit in region.exits: if exit.connected_region: dest_region = ret.get_region(exit.connected_region.name, region.player) From 051fb075a363cb2877c11e784896d35b02029ba7 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 17 Dec 2024 07:40:11 -0600 Subject: [PATCH 24/25] Added Thieves Maiden to followers that have pathing logic --- DoorShuffle.py | 2 +- Doors.py | 1 + Dungeons.py | 2 +- Regions.py | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/DoorShuffle.py b/DoorShuffle.py index 1f2fed26..02ce9226 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -3792,7 +3792,7 @@ logical_connections = [ ('Thieves Conveyor Block Path', 'Thieves Conveyor Bridge'), ("Thieves Blind's Cell Door", "Thieves Blind's Cell Interior"), ("Thieves Blind's Cell Exit", "Thieves Blind's Cell"), - ('Revealing Light', 'Thieves Revealing Light'), + ('Revealing Light', 'Revealing Light'), ('Thieves Town Boss', 'Thieves Boss Spoils'), ('Ice Cross Bottom Push Block Left', 'Ice Floor Switch'), diff --git a/Doors.py b/Doors.py index c5519d13..06d8770a 100644 --- a/Doors.py +++ b/Doors.py @@ -727,6 +727,7 @@ def create_doors(world, player): create_door(player, 'Thieves Big Chest Room ES', Intr).dir(Ea, 0x44, Bot, High).small_key().pos(1), create_door(player, 'Thieves Conveyor Block WN', Intr).dir(We, 0x44, Top, High).pos(0), create_door(player, 'Thieves Trap EN', Intr).dir(Ea, 0x44, Left, Top).pos(0), + create_door(player, 'Revealing Light', Lgcl), create_door(player, 'Thieves Town Boss', Lgcl), create_door(player, 'Ice Lobby SE', Nrml).dir(So, 0x0e, Right, High).pos(2).portal(X, 0x00), diff --git a/Dungeons.py b/Dungeons.py index f7cb4f80..02734038 100644 --- a/Dungeons.py +++ b/Dungeons.py @@ -139,7 +139,7 @@ thieves_regions = [ 'Thieves Attic', 'Thieves Attic Hint', 'Thieves Attic Switch', 'Thieves Cricket Hall Left', 'Thieves Cricket Hall Right', 'Thieves Attic Window', 'Thieves Basement Block', 'Thieves Blocked Entry', 'Thieves Lonely Zazak', "Thieves Blind's Cell", "Thieves Blind's Cell Interior", 'Thieves Conveyor Bridge', - 'Thieves Conveyor Block', 'Thieves Big Chest Room', 'Thieves Trap', 'Thieves Revealing Light', + 'Thieves Conveyor Block', 'Thieves Big Chest Room', 'Thieves Trap', 'Revealing Light', 'Thieves Boss Spoils', 'Thieves Town Portal' ] diff --git a/Regions.py b/Regions.py index fa25fa30..205e05d0 100644 --- a/Regions.py +++ b/Regions.py @@ -718,8 +718,8 @@ def create_dungeon_regions(world, player): create_dungeon_region(player, 'Thieves Compass Room', 'Thieves\' Town', ['Thieves\' Town - Compass Chest'], ['Thieves Compass Room NW Edge', 'Thieves Compass Room N Edge', 'Thieves Compass Room WS Edge', 'Thieves Compass Room W']), create_dungeon_region(player, 'Thieves Big Chest Nook', 'Thieves\' Town', ['Thieves\' Town - Big Key Chest'], ['Thieves Big Chest Nook ES Edge']), create_dungeon_region(player, 'Thieves Hallway', 'Thieves\' Town', ['Thieves\' Town - Hallway Pot Key'], ['Thieves Hallway SE', 'Thieves Hallway NE', 'Thieves Hallway WN', 'Thieves Hallway WS']), - create_dungeon_region(player, 'Thieves Boss', 'Thieves\' Town', None, ['Thieves Boss SE', 'Revealing Light', 'Thieves Town Boss']), - create_dungeon_region(player, 'Thieves Revealing Light', 'Thieves\' Town', ['Revealing Light']), + create_dungeon_region(player, 'Thieves Boss', 'Thieves\' Town', None, ['Thieves Boss SE', 'Revealing Light']), + create_dungeon_region(player, 'Revealing Light', 'Thieves\' Town', ['Revealing Light'], ['Thieves Town Boss']), create_dungeon_region(player, 'Thieves Boss Spoils', 'Thieves\' Town', ['Thieves\' Town - Boss', 'Thieves\' Town - Prize', 'Thieves\' Town - Boss Kill']), create_dungeon_region(player, 'Thieves Pot Alcove Mid', 'Thieves\' Town', None, ['Thieves Pot Alcove Mid ES', 'Thieves Pot Alcove Mid WS']), create_dungeon_region(player, 'Thieves Pot Alcove Bottom', 'Thieves\' Town', None, ['Thieves Pot Alcove Bottom SW']), From 3d002c26f3ed58c2efe6009eb63ce6cee7ef7d85 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 17 Dec 2024 07:51:39 -0600 Subject: [PATCH 25/25] Version bump 0.5.0.6 --- CHANGELOG.md | 9 +++++++++ OverworldShuffle.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcf77f70..62ed719d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 0.5.0.6 +- New improved Pseudoboots behavior +- Fixed issue with missing Blue Potion in Dark Lake Shop in Inverted +- Various generation and logic fixes for HMG/NL +- Fixed error with prize plando +- Fixed error with District ER +- Added TT Maiden pathing to spoiler log +- Corrected and improved some pathing logic + ## 0.5.0.5 - Fixed issue with vanilla HCBK acting like a small key diff --git a/OverworldShuffle.py b/OverworldShuffle.py index e5f24d4f..625e43d2 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -8,7 +8,7 @@ from OWEdges import OWTileRegions, OWEdgeGroups, OWEdgeGroupsTerrain, OWExitType from OverworldGlitchRules import create_owg_connections from Utils import bidict -version_number = '0.5.0.5' +version_number = '0.5.0.6' # branch indicator is intentionally different across branches version_branch = '-u'