diff --git a/.gitignore b/.gitignore index b36abdb5..44e00b30 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,9 @@ *.bmbp *.log *_Spoiler.json +*_custom.yaml +*_meta.txt +*.bps *.pyc *.sfc *.srm diff --git a/BaseClasses.py b/BaseClasses.py index e6456ef7..25cee608 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -2813,6 +2813,11 @@ class Pot(object): item = self.item if not self.indicator else self.standing_item_code return [self.x, high_byte, item] + def get_region(self, world, player): + if world.mode[player] == 'inverted' and self.room == 'Links House': + return world.get_region('Inverted Links House', 1) + return world.get_region(self.room, 1) + def __eq__(self, other): return self.x == other.x and self.y == other.y and self.room == other.room diff --git a/DoorShuffle.py b/DoorShuffle.py index b6e264af..3ced8a44 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -233,7 +233,7 @@ def vanilla_key_logic(world, player): key_layout = build_key_layout(builder, start_regions, doors, world, player) valid = validate_key_layout(key_layout, world, player) if not valid: - logging.getLogger('').warning('Vanilla key layout not valid %s', builder.name) + logging.getLogger('').info('Vanilla key layout not valid %s', builder.name) builder.key_door_proposal = doors if player not in world.key_logic.keys(): world.key_logic[player] = {} @@ -799,11 +799,17 @@ def main_dungeon_pool(dungeon_pool, world, player): else: if 'Hyrule Castle' in pool: hc = world.get_dungeon('Hyrule Castle', player) - hc.dungeon_items.append(ItemFactory('Compass (Escape)', player)) + hc_compass = ItemFactory('Compass (Escape)', player) + hc_compass.advancement = world.restrict_boss_items[player] != 'none' + hc.dungeon_items.append(hc_compass) if 'Agahnims Tower' in pool: at = world.get_dungeon('Agahnims Tower', player) - at.dungeon_items.append(ItemFactory('Compass (Agahnims Tower)', player)) - at.dungeon_items.append(ItemFactory('Map (Agahnims Tower)', player)) + at_compass = ItemFactory('Compass (Agahnims Tower)', player) + at_compass.advancement = world.restrict_boss_items[player] != 'none' + at.dungeon_items.append(at_compass) + at_map = ItemFactory('Map (Agahnims Tower)', player) + at_map.advancement = world.restrict_boss_items[player] != 'none' + at.dungeon_items.append(at_map) sector_pool = convert_to_sectors(region_list, world, player) merge_sectors(sector_pool, world, player) # todo: which dungeon to create @@ -1227,11 +1233,16 @@ def cross_dungeon(world, player): paths = determine_required_paths(world, player) check_required_paths(paths, world, player) + hc_compass = ItemFactory('Compass (Escape)', player) + at_compass = ItemFactory('Compass (Agahnims Tower)', player) + at_map = ItemFactory('Map (Agahnims Tower)', player) + if world.restrict_boss_items[player] != 'none': + hc_compass.advancement = at_compass.advancement = at_map.advancement = True hc = world.get_dungeon('Hyrule Castle', player) - hc.dungeon_items.append(ItemFactory('Compass (Escape)', player)) + hc.dungeon_items.append(hc_compass) at = world.get_dungeon('Agahnims Tower', player) - at.dungeon_items.append(ItemFactory('Compass (Agahnims Tower)', player)) - at.dungeon_items.append(ItemFactory('Map (Agahnims Tower)', player)) + at.dungeon_items.append(at_compass) + at.dungeon_items.append(at_map) setup_custom_door_types(world, player) assign_cross_keys(dungeon_builders, world, player) @@ -3196,8 +3207,10 @@ def find_inaccessible_regions(world, player): def find_accessible_entrances(world, player, builder): entrances = [region.name for region in (portal.door.entrance.parent_region for portal in world.dungeon_portals[player]) if region.dungeon.name == builder.name] entrances.extend(drop_entrances[builder.name]) + hc_std = False if world.mode[player] == 'standard' and builder.name == 'Hyrule Castle': + hc_std = True start_regions = ['Hyrule Castle Courtyard'] elif world.mode[player] != 'inverted': start_regions = ['Links House', 'Sanctuary'] @@ -3222,6 +3235,8 @@ def find_accessible_entrances(world, player, builder): if connect not in queue and connect not in visited_regions: queue.append(connect) for ext in next_region.exits: + if hc_std and ext.name == 'Hyrule Castle Main Gate (North)': # just skip it + continue connect = ext.connected_region if connect is None or ext.door and ext.door.blocked: continue diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 1537967d..658f3e8a 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -63,9 +63,10 @@ def link_entrances(world, player): connect_caves(world, lw_entrances, [], hyrule_castle_exits, player) elif world.doorShuffle[player] != 'vanilla': # sanc is in light world, so must all of HC if door shuffle is on - connect_mandatory_exits(world, lw_entrances, - [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)', 'Hyrule Castle Exit (South)')], + hyrule_castle_exits = [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)', 'Hyrule Castle Exit (South)')] + connect_mandatory_exits(world, lw_entrances, hyrule_castle_exits, list(LW_Dungeon_Entrances_Must_Exit), player) + connect_caves(world, lw_entrances, [], hyrule_castle_exits, player) else: connect_mandatory_exits(world, lw_entrances, dungeon_exits, list(LW_Dungeon_Entrances_Must_Exit), player) connect_mandatory_exits(world, dw_entrances, dungeon_exits, list(DW_Dungeon_Entrances_Must_Exit), player) diff --git a/Fill.py b/Fill.py index 5914b521..a8eecc3a 100644 --- a/Fill.py +++ b/Fill.py @@ -48,6 +48,10 @@ def fill_dungeons_restrictive(world, shuffled_locations): bigs, smalls, others = [], [], [] for i in dungeon_items: (bigs if i.bigkey else smalls if i.smallkey else others).append(i) + unplaced_smalls = list(smalls) + for i in world.itempool: + if i.smallkey and world.keyshuffle[i.player]: + unplaced_smalls.append(i) def fill(base_state, items, key_pool): fill_restrictive(world, base_state, shuffled_locations, items, key_pool, True) @@ -56,12 +60,12 @@ def fill_dungeons_restrictive(world, shuffled_locations): big_state_base = all_state_base.copy() for x in smalls + others: big_state_base.collect(x, True) - fill(big_state_base, bigs, smalls) + fill(big_state_base, bigs, unplaced_smalls) random.shuffle(shuffled_locations) small_state_base = all_state_base.copy() for x in others: small_state_base.collect(x, True) - fill(small_state_base, smalls, list(smalls)) + fill(small_state_base, smalls, unplaced_smalls) random.shuffle(shuffled_locations) fill(all_state_base, others, None) diff --git a/PotShuffle.py b/PotShuffle.py index eafc7399..2848dfe8 100644 --- a/PotShuffle.py +++ b/PotShuffle.py @@ -894,7 +894,7 @@ def shuffle_pots(world, player): new_pot.item = PotItem.FiveRupees if new_pot.item == PotItem.Key: - key = next(location for location in world.get_region(old_pot.room, player).locations if location.name in key_drop_data) + key = next(location for location in old_pot.get_region(world, player).locations if location.name in key_drop_data) key.pot = new_pot if new_pot.room != old_pot.room: # Move pot key to new room @@ -970,7 +970,7 @@ def choose_pots(world, player): dungeon_list = [] for super_tile, pot_list in vanilla_pots.items(): for pot in pot_list: - if world.get_region(pot.room, player).type == RegionType.Cave: + if pot.get_region(world, player).type == RegionType.Cave: pot_pool.add(pot) else: dungeon_list.append(pot) @@ -981,7 +981,7 @@ def choose_pots(world, player): dungeon_count = 0 for super_tile, pot_list in vanilla_pots.items(): for pot in pot_list: - if world.get_region(pot.room, player).type == RegionType.Cave: + if pot.get_region(world, player).type == RegionType.Cave: pot_pool.add(pot) else: dungeon_map[pot.room].append(pot) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index f46e2629..6b394973 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -217,6 +217,8 @@ Same as above but both small keys and bigs keys of the dungeon are not allowed o * Fixed an issue where trinity goal would open pyramid unexpectedly. (No longer does so if ER mdoe is shuffling holes). Crystals goal updated to match that behavior. * Fixed a playthrough issue that was not respecting pot rules * Fixed an issue that was conflicting with downstream OWR project + * Fixed an issue with inverted and certain pottery settings + * Fixed an issue with small keys being shuffled and big keys not (key distribution) * 1.0.1.1 * Fixed the pots in Mire Storyteller/ Dark Desert Hint to be colorized when they should be * Certain pot items no longer reload when reloading the supertile (matches original pot behavior better) diff --git a/Regions.py b/Regions.py index 88c0242b..d0ff68ab 100644 --- a/Regions.py +++ b/Regions.py @@ -1073,9 +1073,9 @@ def valid_pot_location(pot, pot_set, world, player): return True if world.pottery[player] in ['reduced', 'clustered'] and pot in pot_set: return True - if world.pottery[player] == 'dungeon' and world.get_region(pot.room, player).type == RegionType.Dungeon: + if world.pottery[player] == 'dungeon' and pot.get_region(world, player).type == RegionType.Dungeon: return True - if world.pottery[player] in ['cave', 'cavekeys'] and world.get_region(pot.room, player).type == RegionType.Cave: + if world.pottery[player] in ['cave', 'cavekeys'] and pot.get_region(world, player).type == RegionType.Cave: return True return False diff --git a/Rules.py b/Rules.py index a0d78863..d12e7341 100644 --- a/Rules.py +++ b/Rules.py @@ -323,8 +323,10 @@ def global_rules(world, player): # byrna could work with sufficient magic set_rule(world.get_location('Misery Mire - Spike Chest', player), lambda state: (state.world.can_take_damage and state.has_hearts(player, 4)) or state.has('Cane of Byrna', player) or state.has('Cape', player)) loc = world.get_location('Misery Mire - Spikes Pot Key', player) - if loc.pot.x == 48 and loc.pot.y == 28: # pot shuffled to spike area - set_rule(loc, lambda state: (state.world.can_take_damage and state.has_hearts(player, 4)) or state.has('Cane of Byrna', player) or state.has('Cape', player)) + if loc.pot: + if loc.pot.x == 48 and loc.pot.y == 28: # pot shuffled to spike area + set_rule(loc, lambda state: (state.world.can_take_damage and state.has_hearts(player, 4)) + or state.has('Cane of Byrna', player) or state.has('Cape', player)) set_rule(world.get_entrance('Mire Left Bridge Hook Path', player), lambda state: state.has('Hookshot', player)) set_rule(world.get_entrance('Mire Tile Room NW', player), lambda state: state.has_fire_source(player)) set_rule(world.get_entrance('Mire Attic Hint Hole', player), lambda state: state.has_fire_source(player)) diff --git a/source/meta/build-dr.py b/source/meta/build-dr.py index 6f26adb9..a83c9d56 100644 --- a/source/meta/build-dr.py +++ b/source/meta/build-dr.py @@ -22,7 +22,6 @@ if os.path.isdir("build") and not sys.platform.find("mac") and not sys.platform. subprocess.run(" ".join([f"pyinstaller {SPEC_FILE} ", upx_string, "-y ", - "--onefile ", f"--distpath {DEST_DIRECTORY} ", ]), shell=True) diff --git a/source/meta/build-gui.py b/source/meta/build-gui.py index ae284261..4986df67 100644 --- a/source/meta/build-gui.py +++ b/source/meta/build-gui.py @@ -22,7 +22,6 @@ if os.path.isdir("build") and not sys.platform.find("mac") and not sys.platform. subprocess.run(" ".join([f"pyinstaller {SPEC_FILE} ", upx_string, "-y ", - "--onefile ", f"--distpath {DEST_DIRECTORY} ", ]), shell=True)