diff --git a/DoorShuffle.py b/DoorShuffle.py index ee9cbd01..c70cc040 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -14,7 +14,7 @@ from RoomData import DoorKind, PairedDoor, reset_rooms from DungeonGenerator import ExplorationState, convert_regions, generate_dungeon, pre_validate, determine_required_paths, drop_entrances from DungeonGenerator import create_dungeon_builders, split_dungeon_builder, simple_dungeon_builder, default_dungeon_entrances from DungeonGenerator import dungeon_portals, dungeon_drops, GenerationException -from KeyDoorShuffle import analyze_dungeon, build_key_layout, validate_key_layout +from KeyDoorShuffle import analyze_dungeon, build_key_layout, validate_key_layout, determine_prize_lock from Utils import ncr, kth_combination @@ -1463,6 +1463,7 @@ def find_valid_combination(builder, start_regions, world, player, drop_keys=True start_regions = [x for x in start_regions if x not in excluded.keys()] key_layout = build_key_layout(builder, start_regions, proposal, world, player) + determine_prize_lock(key_layout, world, player) while not validate_key_layout(key_layout, world, player): itr += 1 stop_early = False diff --git a/DungeonGenerator.py b/DungeonGenerator.py index 12aaa1c9..f88499bd 100644 --- a/DungeonGenerator.py +++ b/DungeonGenerator.py @@ -56,11 +56,6 @@ def pre_validate(builder, entrance_region_names, split_dungeon, world, player): def generate_dungeon(builder, entrance_region_names, split_dungeon, world, player): - sector = generate_dungeon_main(builder, entrance_region_names, split_dungeon, world, player) - return sector - - -def generate_dungeon_main(builder, entrance_region_names, split_dungeon, world, player): if builder.valid_proposal: # we made this earlier in gen, just use it proposed_map = builder.valid_proposal else: @@ -99,6 +94,15 @@ def generate_dungeon_find_proposal(builder, entrance_region_names, split_dungeon if (access_region.name in world.inaccessible_regions[player] and region.name not in world.enabled_entrances[player]): excluded[region] = None + elif len(region.entrances) == 1: # for holes + access_region = next(x.parent_region for x in region.entrances + if x.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld] + or x.parent_region.name == 'Sewer Drop') + if access_region.name == 'Sewer Drop': + access_region = next(x.parent_region for x in access_region.entrances) + if (access_region.name in world.inaccessible_regions[player] and + region.name not in world.enabled_entrances[player]): + excluded[region] = None entrance_regions = [x for x in entrance_regions if x not in excluded.keys()] doors_to_connect = {} all_regions = set() diff --git a/KeyDoorShuffle.py b/KeyDoorShuffle.py index 1b74ae80..a84fe5e8 100644 --- a/KeyDoorShuffle.py +++ b/KeyDoorShuffle.py @@ -27,6 +27,7 @@ class KeyLayout(object): self.found_doors = set() self.prize_relevant = None + self.prize_can_lock = None # if true, then you may need to beat the bo # bk special? # bk required? True if big chests or big doors exists @@ -1447,7 +1448,10 @@ def validate_key_layout_sub_loop(key_layout, state, checked_states, flat_proposa found_forced_bk = state.found_forced_bk() smalls_done = not smalls_avail or not enough_small_locations(state, available_small_locations) bk_done = state.big_key_opened or num_bigs == 0 or (available_big_locations == 0 and not found_forced_bk) - prize_done = not key_layout.prize_relevant or state.prize_doors_opened + # prize door should not be opened if the boss is reachable - but not reached yet + allow_for_prize_lock = (key_layout.prize_can_lock and + not any(x for x in state.found_locations if '- Prize' in x.name)) + prize_done = not key_layout.prize_relevant or state.prize_doors_opened or allow_for_prize_lock if smalls_done and bk_done and prize_done: return False else: @@ -1531,6 +1535,39 @@ def enough_small_locations(state, avail_small_loc): return avail_small_loc >= len(unique_d_set) +def determine_prize_lock(key_layout, world, player): + if ((world.retro[player] and (world.mode[player] != 'standard' or key_layout.sector.name != 'Hyrule Castle')) + or world.logic[player] == 'nologic'): + return # done, doesn't matter what + flat_proposal = key_layout.flat_prop + state = ExplorationState(dungeon=key_layout.sector.name) + state.key_locations = key_layout.max_chests + state.big_key_special = check_bk_special(key_layout.sector.regions, world, player) + prize_lock_possible = False + for region in key_layout.start_regions: + dungeon_entrance, portal_door = find_outside_connection(region) + prize_relevant_flag = prize_relevance(key_layout, dungeon_entrance) + if prize_relevant_flag: + state.append_door_to_list(portal_door, state.prize_doors) + state.prize_door_set[portal_door] = dungeon_entrance + key_layout.prize_relevant = prize_relevant_flag + prize_lock_possible = True + else: + state.visit_region(region, key_checks=True) + state.add_all_doors_check_keys(region, flat_proposal, world, player) + if not prize_lock_possible: + return # done, no prize entrances to worry about + expand_key_state(state, flat_proposal, world, player) + while len(state.small_doors) > 0 or len(state.big_doors) > 0: + if len(state.big_doors) > 0: + open_a_door(state.big_doors[0].door, state, flat_proposal, world, player) + elif len(state.small_doors) > 0: + open_a_door(state.small_doors[0].door, state, flat_proposal, world, player) + expand_key_state(state, flat_proposal, world, player) + if any(x for x in state.found_locations if '- Prize' in x.name): + key_layout.prize_can_lock = True + + def cnt_avail_small_locations(free_locations, key_only, state, world, player): if not world.keyshuffle[player] and not world.retro[player]: bk_adj = 1 if state.big_key_opened and not state.big_key_special else 0