diff --git a/BaseClasses.py b/BaseClasses.py index edef87bd..23e8cc9d 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -814,6 +814,15 @@ class CollectionState(object): rrp[k] = missing_regions[k] possible_path = terminal_states[0].path[k] self.path[k] = paths[k] = possible_path + for conn in k.exits: + if self.is_small_door(conn): + door = conn.door if conn.door.smallKey else conn.door.controller + key_logic = self.world.key_logic[player][dungeon_name] + if door.name not in self.reached_doors[player]: + self.door_counter[player][0][dungeon_name] += 1 + self.reached_doors[player].add(door.name) + if key_logic.sm_doors[door]: + self.reached_doors[player].add(key_logic.sm_doors[door].name) missing_bc = {} for blocked, crystal in common_bc.items(): if (blocked not in bc and blocked.parent_region in rrp @@ -3076,6 +3085,7 @@ class Spoiler(object): outfile.write('Enemy Health:'.ljust(line_width) + '%s\n' % self.metadata['enemy_health'][player]) outfile.write('Enemy Damage:'.ljust(line_width) + '%s\n' % self.metadata['enemy_damage'][player]) outfile.write('Hints:'.ljust(line_width) + '%s\n' % yn(self.metadata['hints'][player])) + outfile.write('Race:'.ljust(line_width) + '%s\n' % yn(self.world.settings.world_rep['meta']['race'])) if self.startinventory: outfile.write('Starting Inventory:'.ljust(line_width)) diff --git a/CLI.py b/CLI.py index 7210c080..5fea49d3 100644 --- a/CLI.py +++ b/CLI.py @@ -122,6 +122,11 @@ def parse_cli(argv, no_defaults=False): defaults = copy.deepcopy(ret) for player in range(1, player_num + 1): playerargs = parse_cli(shlex.split(getattr(ret, f"p{player}")), True) + + if playerargs.filename: + playersettings = apply_settings_file({}, playerargs.filename) + for k, v in playersettings.items(): + setattr(playerargs, k, v) for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality', 'ow_shuffle', 'ow_terrain', 'ow_crossed', 'ow_keepsimilar', 'ow_mixed', 'ow_whirlpool', 'ow_fluteshuffle', diff --git a/DoorShuffle.py b/DoorShuffle.py index 4433714d..f52365f3 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -477,14 +477,14 @@ def choose_portals(world, player): info.required_passage = {x: y for x, y in info.required_passage.items() if len(y) > 0} for target_region, possible_portals in info.required_passage.items(): candidates = find_portal_candidates(master_door_list, dungeon, custom, allowed, need_passage=True, - bk_shuffle=bk_shuffle, rupee_bow=rupee_bow_flag) + bk_shuffle=bk_shuffle, standard=std_flag, rupee_bow=rupee_bow_flag) choice, portal = assign_portal(candidates, possible_portals, custom, world, player) portal.destination = True clean_up_portal_assignment(portal_assignment, dungeon, portal, master_door_list, outstanding_portals) dead_end_choices = info.total - 1 - len(portal_assignment[dungeon]) for i in range(0, dead_end_choices): candidates = find_portal_candidates(master_door_list, dungeon, custom, allowed, dead_end_allowed=True, - bk_shuffle=bk_shuffle, rupee_bow=rupee_bow_flag) + bk_shuffle=bk_shuffle, standard=std_flag, rupee_bow=rupee_bow_flag) possible_portals = outstanding_portals if not info.sole_entrance else [x for x in outstanding_portals if x != info.sole_entrance] choice, portal = assign_portal(candidates, possible_portals, custom, world, player) if choice.deadEnd: @@ -796,15 +796,18 @@ def main_dungeon_pool(dungeon_pool, world, player): hc = world.get_dungeon('Hyrule Castle', player) hc_compass = ItemFactory('Compass (Escape)', player) hc_compass.advancement = world.restrict_boss_items[player] != 'none' - hc.dungeon_items.append(hc_compass) + if hc.dungeon_items.count(hc_compass) < 1: + hc.dungeon_items.append(hc_compass) if 'Agahnims Tower' in pool: at = world.get_dungeon('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) + if at.dungeon_items.count(at_compass) < 1: + 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) + if at.dungeon_items.count(at_map) < 1: + 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 @@ -1238,10 +1241,13 @@ def cross_dungeon(world, 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(hc_compass) + if hc.dungeon_items.count(hc_compass) < 1: + hc.dungeon_items.append(hc_compass) at = world.get_dungeon('Agahnims Tower', player) - at.dungeon_items.append(at_compass) - at.dungeon_items.append(at_map) + if at.dungeon_items.count(at_compass) < 1: + at.dungeon_items.append(at_compass) + if at.dungeon_items.count(at_map) < 1: + at.dungeon_items.append(at_map) setup_custom_door_types(world, player) assign_cross_keys(dungeon_builders, world, player) diff --git a/Doors.py b/Doors.py index 110ca5c9..edc51ac0 100644 --- a/Doors.py +++ b/Doors.py @@ -1521,6 +1521,7 @@ def create_doors(world, player): world.get_door("GT Bob\'s Room SE", player).passage = False world.get_door('Desert Tiles 2 SE', player).bk_shuffle_req = True # key-drop note: allows this to be a portal world.get_door('Swamp Lobby S', player).standard_restricted = True + world.get_door('Sanctuary S', player).standard_restricted = True world.get_door('PoD Mimics 2 SW', player).rupee_bow_restricted = True # bow statue # enemizer logic could get rid of the following restriction world.get_door('PoD Pit Room S', player).rupee_bow_restricted = True # so mimics 1 shouldn't be required diff --git a/Fill.py b/Fill.py index 7446f783..9648ca62 100644 --- a/Fill.py +++ b/Fill.py @@ -386,6 +386,11 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None random.shuffle(fill_locations) random.shuffle(world.itempool) + if world.item_pool_config.preferred: + pref = list(world.item_pool_config.preferred.keys()) + pref_len = len(pref) + world.itempool.sort(key=lambda i: pref_len - pref.index((i.name, i.player)) + if (i.name, i.player) in world.item_pool_config.preferred else 0) progitempool = [item for item in world.itempool if item.advancement] prioitempool = [item for item in world.itempool if not item.advancement and item.priority] restitempool = [item for item in world.itempool if not item.advancement and not item.priority] diff --git a/InitialSram.py b/InitialSram.py index fa94d448..d7d79a18 100644 --- a/InitialSram.py +++ b/InitialSram.py @@ -145,8 +145,8 @@ class InitialSram: 'Big Key (Misery Mire)': (0x367, 0x01), 'Compass (Misery Mire)': (0x365, 0x01), 'Map (Misery Mire)': (0x369, 0x01), 'Big Key (Turtle Rock)': (0x366, 0x08), 'Compass (Turtle Rock)': (0x364, 0x08), 'Map (Turtle Rock)': (0x368, 0x08), 'Big Key (Ganons Tower)': (0x366, 0x04), 'Compass (Ganons Tower)': (0x364, 0x04), 'Map (Ganons Tower)': (0x368, 0x04)} - set_or_table = {'Flippers': (0x356, 1, 0x379, 0x02),'Pegasus Boots': (0x355, 1, 0x379, 0x04), - 'Shovel': (0x34C, 1, 0x38C, 0x04), 'Ocarina': (0x34C, 3, 0x38C, 0x01), + set_or_table = {'Flippers': (0x356, 1, 0x379, 0x02), 'Pegasus Boots': (0x355, 1, 0x379, 0x04), + 'Shovel': (0x34C, 1, 0x38C, 0x04), 'Ocarina': (0x34C, 2, 0x38C, 0x02), 'Ocarina (Activated)': (0x34C, 3, 0x38C, 0x01), 'Mushroom': (0x344, 1, 0x38C, 0x20 | 0x08), 'Magic Powder': (0x344, 2, 0x38C, 0x10), 'Blue Boomerang': (0x341, 1, 0x38C, 0x80), 'Red Boomerang': (0x341, 2, 0x38C, 0x40)} diff --git a/Main.py b/Main.py index e15e66ae..959afc01 100644 --- a/Main.py +++ b/Main.py @@ -35,7 +35,7 @@ from source.overworld.EntranceShuffle2 import link_entrances_new from source.tools.BPS import create_bps_from_data from source.classes.CustomSettings import CustomSettings -__version__ = '1.2.0.5-u' +__version__ = '1.2.0.7-u' from source.classes.BabelFish import BabelFish diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 095682e0..597cdc2f 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -108,6 +108,15 @@ These are now independent of retro mode and have three options: None, Random, an * Bonk Fairy (Dark) # Bug Fixes and Notes +* 1.2.0.7-u + * Fix for some misery mire key logic + * Minor standard generation fix + * Fix for inactive flute start + * Settingsfile for multiworld generation support + * Fix for duped HC/AT Maps/Compasses +* 1.2.0.6-u + * Fix for light cone in Escape when entering from Dark World post-zelda + * Fix for light cone in Escape when lighting a torch with fire rod * 1.2.0.5.u * Logic fix for Sanctuary mirror (it wasn't resetting the crystal state) * Minor bugfixes for customizer diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index e35e1e78..5b5526a9 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -632,7 +632,7 @@ def do_fixed_shuffle(avail, entrance_list): rules = Restrictions() rules.size = size if ('Hyrule Castle Entrance (South)' in entrances and - avail.world.doorShuffle[avail.player] in ['basic', 'crossed']): + avail.world.doorShuffle[avail.player] != 'vanilla'): rules.must_exit_to_lw = True if 'Inverted Ganons Tower' in entrances and not avail.world.shuffle_ganon: rules.fixed = True @@ -1025,12 +1025,15 @@ def connect_custom(avail_pool, world, player): if world.customizer and world.customizer.get_entrances(): custom_entrances = world.customizer.get_entrances() player_key = player - for ent_name, exit_name in custom_entrances[player_key]['two-way'].items(): - connect_two_way(ent_name, exit_name, avail_pool) - for ent_name, exit_name in custom_entrances[player_key]['entrances'].items(): - connect_entrance(ent_name, exit_name, avail_pool) - for ent_name, exit_name in custom_entrances[player_key]['exits'].items(): - connect_exit(exit_name, ent_name, avail_pool) + if 'two-way' in custom_entrances[player_key]: + for ent_name, exit_name in custom_entrances[player_key]['two-way'].items(): + connect_two_way(ent_name, exit_name, avail_pool) + if 'entrances' in custom_entrances[player_key]: + for ent_name, exit_name in custom_entrances[player_key]['entrances'].items(): + connect_entrance(ent_name, exit_name, avail_pool) + if 'exits' in custom_entrances[player_key]: + for ent_name, exit_name in custom_entrances[player_key]['exits'].items(): + connect_exit(exit_name, ent_name, avail_pool) def connect_simple(world, exit_name, region_name, player):