diff --git a/BaseClasses.py b/BaseClasses.py index 1a06ef6b..1de23bc1 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -559,7 +559,9 @@ class MultiWorld(): else: return all((self.has_beaten_game(state, p) for p in range(1, self.players + 1))) - def can_beat_game(self, starting_state: Optional[CollectionState] = None) -> bool: + def can_beat_game(self, + starting_state: Optional[CollectionState] = None, + locations: Optional[Iterable[Location]] = None) -> bool: if starting_state: if self.has_beaten_game(starting_state): return True @@ -568,7 +570,9 @@ class MultiWorld(): state = CollectionState(self) if self.has_beaten_game(state): return True - prog_locations = {location for location in self.get_locations() if location.item + + base_locations = self.get_locations() if locations is None else locations + prog_locations = {location for location in base_locations if location.item and location.item.advancement and location not in state.locations_checked} while prog_locations: @@ -1603,21 +1607,19 @@ class Spoiler: # in the second phase, we cull each sphere such that the game is still beatable, # reducing each range of influence to the bare minimum required inside it - restore_later: Dict[Location, Item] = {} + required_locations = {location for sphere in collection_spheres for location in sphere} for num, sphere in reversed(tuple(enumerate(collection_spheres))): to_delete: Set[Location] = set() for location in sphere: - # we remove the item at location and check if game is still beatable + # we remove the location from required_locations to sweep from, and check if the game is still beatable logging.debug('Checking if %s (Player %d) is required to beat the game.', location.item.name, location.item.player) - old_item = location.item - location.item = None - if multiworld.can_beat_game(state_cache[num]): + required_locations.remove(location) + if multiworld.can_beat_game(state_cache[num], required_locations): to_delete.add(location) - restore_later[location] = old_item else: # still required, got to keep it around - location.item = old_item + required_locations.add(location) # cull entries in spheres for spoiler walkthrough at end sphere -= to_delete @@ -1634,7 +1636,7 @@ class Spoiler: logging.debug('Checking if %s (Player %d) is required to beat the game.', item.name, item.player) precollected_items.remove(item) multiworld.state.remove(item) - if not multiworld.can_beat_game(): + if not multiworld.can_beat_game(multiworld.state, required_locations): # Add the item back into `precollected_items` and collect it into `multiworld.state`. multiworld.push_precollected(item) else: @@ -1676,9 +1678,6 @@ class Spoiler: self.create_paths(state, collection_spheres) # repair the multiworld again - for location, item in restore_later.items(): - location.item = item - for item in removed_precollected: multiworld.push_precollected(item)