diff --git a/Fill.py b/Fill.py index 779e1ec4..2cf1f25e 100644 --- a/Fill.py +++ b/Fill.py @@ -245,7 +245,11 @@ def valid_key_placement(item, location, itempool, world): return True key_logic = world.key_logic[item.player][dungeon.name] unplaced_keys = len([x for x in itempool if x.name == key_logic.small_key_name and x.player == item.player]) - return key_logic.check_placement(unplaced_keys, location if item.bigkey else None) + prize_loc = None + if key_logic.prize_location: + prize_loc = world.get_location(key_logic.prize_location, location.player) + cr_count = world.crystals_needed_for_gt[location.player] + return key_logic.check_placement(unplaced_keys, location if item.bigkey else None, prize_loc, cr_count) else: inside_dungeon_item = ((item.smallkey and not world.keyshuffle[item.player]) or (item.bigkey and not world.bigkeyshuffle[item.player])) diff --git a/KeyDoorShuffle.py b/KeyDoorShuffle.py index 1ff840f6..1b74ae80 100644 --- a/KeyDoorShuffle.py +++ b/KeyDoorShuffle.py @@ -58,10 +58,11 @@ class KeyLogic(object): self.outside_keys = 0 self.dungeon = dungeon_name self.sm_doors = {} + self.prize_location = None - def check_placement(self, unplaced_keys, big_key_loc=None): + def check_placement(self, unplaced_keys, big_key_loc=None, prize_loc=None, cr_count=7): for rule in self.placement_rules: - if not rule.is_satisfiable(self.outside_keys, unplaced_keys, big_key_loc): + if not rule.is_satisfiable(self.outside_keys, unplaced_keys, big_key_loc, prize_loc, cr_count): return False if big_key_loc: for rule_a, rule_b in itertools.combinations(self.placement_rules, 2): @@ -120,6 +121,7 @@ class PlacementRule(object): self.check_locations_wo_bk = None self.bk_relevant = True self.key_reduced = False + self.prize_relevance = None def contradicts(self, rule, unplaced_keys, big_key_loc): bk_blocked = big_key_loc in self.bk_conditional_set if self.bk_conditional_set else False @@ -154,7 +156,14 @@ class PlacementRule(object): left -= rule_needed return False - def is_satisfiable(self, outside_keys, unplaced_keys, big_key_loc): + def is_satisfiable(self, outside_keys, unplaced_keys, big_key_loc, prize_location, cr_count): + if self.prize_relevance and prize_location: + if self.prize_relevance == 'BigBomb': + if prize_location.item.name not in ['Crystal 5', 'Crystal 6']: + return True + elif self.prize_relevance == 'GT': + if 'Crystal' not in prize_location.item.name or cr_count < 7: + return True bk_blocked = False if self.bk_conditional_set: for loc in self.bk_conditional_set: @@ -258,6 +267,7 @@ def analyze_dungeon(key_layout, world, player): find_bk_locked_sections(key_layout, world, player) key_logic.bk_chests.update(find_big_chest_locations(key_layout.all_chest_locations)) key_logic.bk_chests.update(find_big_key_locked_locations(key_layout.all_chest_locations)) + key_logic.prize_location = dungeon_table[key_layout.sector.name].prize if world.retro[player] and world.mode[player] != 'standard': return @@ -361,6 +371,7 @@ def create_exhaustive_placement_rules(key_layout, world, player): rule.bk_conditional_set = blocked_loc rule.needed_keys_wo_bk = min_keys rule.check_locations_wo_bk = set(filter_big_chest(accessible_loc)) + rule.prize_relevance = key_layout.prize_relevant if rule_prize_relevant(key_counter) else None if valid_rule: key_logic.placement_rules.append(rule) adjust_locations_rules(key_logic, rule, accessible_loc, key_layout, key_counter, max_ctr) @@ -368,6 +379,10 @@ def create_exhaustive_placement_rules(key_layout, world, player): refine_location_rules(key_layout) +def rule_prize_relevant(key_counter): + return not key_counter.prize_doors_opened and not key_counter.prize_received + + 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 @@ -467,7 +482,7 @@ def refine_placement_rules(key_layout, max_ctr): if rule.needed_keys_wo_bk == 0: rules_to_remove.append(rule) if len(rule.check_locations_wo_bk) < rule.needed_keys_wo_bk or rule.needed_keys_wo_bk > key_layout.max_chests: - if len(rule.bk_conditional_set) > 0: + if not rule.prize_relevance and len(rule.bk_conditional_set) > 0: key_logic.bk_restricted.update(rule.bk_conditional_set) rules_to_remove.append(rule) changed = True # impossible for bk to be here, I think @@ -1432,7 +1447,8 @@ 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) - if smalls_done and bk_done: + prize_done = not key_layout.prize_relevant or state.prize_doors_opened + if smalls_done and bk_done and prize_done: return False else: # todo: pretty sure you should OR these paths together, maybe when there's one location and it can @@ -1468,6 +1484,7 @@ def validate_key_layout_sub_loop(key_layout, state, checked_states, flat_proposa if not valid: return False # todo: feel like you only open these if the boss is available??? + # todo: or if a crystal isn't valid placement on this boss 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)