diff --git a/DoorShuffle.py b/DoorShuffle.py index 02f54069..89e7a6d6 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -1823,6 +1823,10 @@ def find_inaccessible_regions(world, player): if connect.type is not RegionType.Dungeon or connect.name.endswith(' Portal'): queue.append(connect) world.inaccessible_regions[player].extend([r.name for r in all_regions.difference(visited_regions) if valid_inaccessible_region(r)]) + if world.mode[player] == 'inverted': + ledge = world.get_region('Hyrule Castle Ledge', 1) + if any(x for x in ledge.exits if x.connected_region.name == 'Agahnims Tower Portal'): + world.inaccessible_regions[player].append('Hyrule Castle Ledge') logger = logging.getLogger('') logger.debug('Inaccessible Regions:') for r in world.inaccessible_regions[player]: diff --git a/DungeonGenerator.py b/DungeonGenerator.py index 86be2a04..47922be5 100644 --- a/DungeonGenerator.py +++ b/DungeonGenerator.py @@ -1113,9 +1113,9 @@ def valid_region_to_explore_in_regions(region, all_regions, world, player): def valid_region_to_explore(region, name, world, player): if region is None: return False - return (region.type == RegionType.Dungeon and region.dungeon.name in name)\ - or region.name in world.inaccessible_regions[player]\ - or (region.name == 'Hyrule Castle Ledge' and world.mode[player] == 'standard') + return ((region.type == RegionType.Dungeon and region.dungeon and region.dungeon.name in name) + or region.name in world.inaccessible_regions[player] + or (region.name == 'Hyrule Castle Ledge' and world.mode[player] == 'standard')) def get_doors(world, region, player): diff --git a/Dungeons.py b/Dungeons.py index eca190c1..e3b76c28 100644 --- a/Dungeons.py +++ b/Dungeons.py @@ -393,11 +393,9 @@ dungeon_bigs = { } dungeon_prize = { - 'Hyrule Castle': None, 'Eastern Palace': 'Eastern Palace - Prize', 'Desert Palace': 'Desert Palace - Prize', 'Tower of Hera': 'Tower of Hera - Prize', - 'Agahnims Tower': None, 'Palace of Darkness': 'Palace of Darkness - Prize', 'Swamp Palace': 'Swamp Palace - Prize', 'Skull Woods': 'Skull Woods - Prize', @@ -405,7 +403,6 @@ dungeon_prize = { 'Ice Palace': 'Ice Palace - Prize', 'Misery Mire': 'Misery Mire - Prize', 'Turtle Rock': 'Turtle Rock - Prize', - 'Ganons Tower': None } dungeon_hints = { diff --git a/Fill.py b/Fill.py index 29226afd..d75cf080 100644 --- a/Fill.py +++ b/Fill.py @@ -722,7 +722,7 @@ def balance_money_progression(world): if room not in rooms_visited[player] and world.get_region(room, player) in state.reachable_regions[player]: wallet[player] += income rooms_visited[player].add(room) - if checked_locations: + if checked_locations or len(unchecked_locations) == 0: if world.has_beaten_game(state): done = True continue @@ -732,7 +732,7 @@ def balance_money_progression(world): solvent = set() insolvent = set() for player in range(1, world.players+1): - if wallet[player] >= sphere_costs[player] > 0: + if wallet[player] >= sphere_costs[player] >= 0: solvent.add(player) if sphere_costs[player] > 0 and sphere_costs[player] > wallet[player]: insolvent.add(player) diff --git a/KeyDoorShuffle.py b/KeyDoorShuffle.py index 0ecb98e4..4c591304 100644 --- a/KeyDoorShuffle.py +++ b/KeyDoorShuffle.py @@ -307,7 +307,7 @@ def create_exhaustive_placement_rules(key_layout, world, player): key_logic = key_layout.key_logic max_ctr = find_max_counter(key_layout) for code, key_counter in key_layout.key_counters.items(): - if key_counter.prize_received and not key_counter.prize_doors_opened: + if skip_key_counter_due_to_prize(key_layout, key_counter): continue # we have the prize, we are not concerned about this case accessible_loc = set() accessible_loc.update(key_counter.free_locations) @@ -343,6 +343,10 @@ def create_exhaustive_placement_rules(key_layout, world, player): refine_location_rules(key_layout) +def skip_key_counter_due_to_prize(key_layout, key_counter): + return key_layout.prize_relevant and key_counter.prize_received and not key_counter.prize_doors_opened + + def placement_self_lock_adjustment(rule, max_ctr, blocked_loc, ctr, world, player): if len(blocked_loc) == 1 and world.accessibility[player] != 'locations': blocked_others = set(max_ctr.other_locations).difference(set(ctr.other_locations)) @@ -1356,7 +1360,8 @@ def validate_key_layout(key_layout, world, player): state.big_key_special = check_bk_special(key_layout.sector.regions, world, player) for region in key_layout.start_regions: dungeon_entrance, portal_door = find_outside_connection(region) - if dungeon_entrance and dungeon_entrance.name in ['Ganons Tower', 'Inverted Ganons Tower', 'Pyramid Fairy']: + if (key_layout.prize_relevant and dungeon_entrance and + dungeon_entrance.name in ['Ganons Tower', 'Inverted Ganons Tower', 'Pyramid Fairy']): state.append_door_to_list(portal_door, state.prize_doors) state.prize_door_set[portal_door] = dungeon_entrance else: @@ -1396,7 +1401,7 @@ def validate_key_layout_sub_loop(key_layout, state, checked_states, flat_proposa state_copy.used_smalls += 1 if state_copy.used_smalls > ttl_small_key_only: state_copy.used_locations += 1 - code = state_id(state_copy, flat_proposal) + code = validate_id(state_copy, flat_proposal) if code not in checked_states.keys(): valid = validate_key_layout_sub_loop(key_layout, state_copy, checked_states, flat_proposal, state, available_small_locations, world, player) @@ -1410,7 +1415,7 @@ def validate_key_layout_sub_loop(key_layout, state, checked_states, flat_proposa open_a_door(state.big_doors[0].door, state_copy, flat_proposal, world, player) if not found_forced_bk: state_copy.used_locations += 1 - code = state_id(state_copy, flat_proposal) + code = validate_id(state_copy, flat_proposal) if code not in checked_states.keys(): valid = validate_key_layout_sub_loop(key_layout, state_copy, checked_states, flat_proposal, state, available_small_locations, world, player) @@ -1422,7 +1427,7 @@ def validate_key_layout_sub_loop(key_layout, state, checked_states, flat_proposa if not state.prize_doors_opened and key_layout.prize_relevant: state_copy = state.copy() open_a_door(next(iter(state_copy.prize_door_set)), state_copy, flat_proposal, world, player) - code = state_id(state_copy, flat_proposal) + code = validate_id(state_copy, flat_proposal) if code not in checked_states.keys(): valid = validate_key_layout_sub_loop(key_layout, state_copy, checked_states, flat_proposal, state, available_small_locations, world, player) @@ -1496,7 +1501,9 @@ def create_key_counters(key_layout, world, player): special_region = region for region in key_layout.start_regions: dungeon_entrance, portal_door = find_outside_connection(region) - if dungeon_entrance and dungeon_entrance.name in ['Ganons Tower', 'Inverted Ganons Tower', 'Pyramid Fairy']: + if (len(key_layout.start_regions) > 1 and dungeon_entrance and + dungeon_entrance.name in ['Ganons Tower', 'Inverted Ganons Tower', 'Pyramid Fairy'] + and key_layout.key_logic.dungeon in dungeon_prize): state.append_door_to_list(portal_door, state.prize_doors) state.prize_door_set[portal_door] = dungeon_entrance key_layout.prize_relevant = True @@ -1616,6 +1623,16 @@ def state_id(state, flat_proposal): return s_id +def validate_id(state, flat_proposal): + s_id = '1' if state.big_key_opened else '0' + for d in flat_proposal: + s_id += '1' if d in state.opened_doors else '0' + if len(state.prize_door_set) > 0: + s_id += '1' if state.prize_doors_opened else '0' + s_id += str(state.used_locations) + return s_id + + def find_counter(opened_doors, bk_hint, key_layout, prize_flag, raise_on_error=True): counter = find_counter_hint(opened_doors, bk_hint, key_layout, prize_flag) if counter is not None: @@ -1878,7 +1895,7 @@ def validate_key_placement(key_layout, world, player): len(counter.key_only_locations) + keys_outside if key_layout.prize_relevant: found_prize = any(x for x in counter.important_locations if '- Prize' in x.name) - if not found_prize: + if not found_prize and key_layout.sector.name in dungeon_prize: prize_loc = world.get_location(dungeon_prize[key_layout.sector.name], player) # todo: pyramid fairy only care about crystals 5 & 6 found_prize = 'Crystal' not in prize_loc.item.name