def add_artifacts( resource_database: ResourceDatabase, mode: LayoutArtifactMode, artifact_minimum_progression: int, ) -> PoolResults: """ :param resource_database: :param mode :param artifact_minimum_progression :return: """ item_pool: List[PickupEntry] = [] initial_resources = ResourceCollection.with_database(resource_database) artifacts_to_place = mode.value for i in range(artifacts_to_place): item_pool.append( pickup_creator.create_artifact(i, artifact_minimum_progression, resource_database)) first_automatic_artifact = artifacts_to_place for automatic_artifact in range(first_automatic_artifact, 12): initial_resources.add_resource_gain( pickup_creator.create_artifact(automatic_artifact, artifact_minimum_progression, resource_database).all_resources, ) return PoolResults(item_pool, {}, initial_resources)
def calculate_pool_results(layout_configuration: BaseConfiguration, resource_database: ResourceDatabase, base_patches: GamePatches = None, rng: Random = None, rng_required: bool = False ) -> PoolResults: """ Creates a PoolResults with all starting items and pickups in fixed locations, as well as a list of pickups we should shuffle. :param layout_configuration: :param resource_database: :return: """ base_results = PoolResults([], {}, ResourceCollection.with_database(resource_database)) # Adding major items to the pool _extend_pool_results(base_results, add_major_items(resource_database, layout_configuration.major_items_configuration, layout_configuration.ammo_configuration)) # Adding ammo to the pool base_results.pickups.extend(add_ammo(resource_database, layout_configuration.ammo_configuration)) try: layout_configuration.game.generator.item_pool_creator( base_results, layout_configuration, resource_database, base_patches, rng, ) except MissingRng as e: if rng_required: raise e return base_results
def find_invalid_strongly_connected_components(game: GameDescription) -> Iterator[str]: import networkx graph = networkx.DiGraph() for node in game.world_list.iterate_nodes(): if isinstance(node, DockLockNode): continue graph.add_node(node) context = NodeContext( patches=GamePatches.create_from_game(game, 0, None), current_resources=ResourceCollection.with_database(game.resource_database), database=game.resource_database, node_provider=game.world_list, ) for node in game.world_list.iterate_nodes(): if node not in graph: continue for other, req in game.world_list.potential_nodes_from(node, context): if other not in graph: continue if req != Requirement.impossible(): graph.add_edge(node, other) starting_node = game.world_list.resolve_teleporter_connection(game.starting_location) for strong_comp in networkx.strongly_connected_components(graph): components: set[Node] = strong_comp # The starting location determines the default component if starting_node in components: continue if any(node.extra.get("different_strongly_connected_component", False) for node in components): continue if len(components) == 1: node = next(iter(components)) # If the component is a single node which is the default node of it's area, allow it area = game.world_list.nodes_to_area(node) if area.default_node == node.name: continue # We accept nodes that have no paths out or in. if not graph.in_edges(node) and not graph.edges(node): continue names = sorted( game.world_list.node_name(node, with_world=True) for node in strong_comp ) yield "Unknown strongly connected component detected containing {} nodes:\n{}".format(len(names), names)
def test_create_starting_popup_empty(default_echoes_configuration, echoes_resource_database): starting_items = ResourceCollection.with_database(echoes_resource_database) # Run result = patch_data_factory._create_starting_popup( default_echoes_configuration, echoes_resource_database, starting_items) # Assert assert result == []
def add_energy_cells(resource_database: ResourceDatabase, ) -> PoolResults: """ :param resource_database: :return: """ item_pool: List[PickupEntry] = [] for i in range(9): item_pool.append( pickup_creator.create_energy_cell(i, resource_database)) return PoolResults(item_pool, {}, ResourceCollection.with_database(resource_database))
def _resources_to_give_for_pickup( self, pickup: PickupEntry, inventory: Inventory, ) -> tuple[str, ResourceCollection]: inventory_resources = ResourceCollection.with_database( self.game.resource_database) inventory_resources.add_resource_gain([ (item, inv_item.capacity) for item, inv_item in inventory.items() ]) conditional = pickup.conditional_for_resources(inventory_resources) if conditional.name is not None: item_name = conditional.name else: item_name = pickup.name resources_to_give = ResourceCollection.with_database( self.game.resource_database) if pickup.respects_lock and not pickup.unlocks_resource and ( pickup.resource_lock is not None and inventory_resources[pickup.resource_lock.locked_by] == 0): pickup_resources = list( pickup.resource_lock.convert_gain(conditional.resources)) item_name = f"Locked {item_name}" else: pickup_resources = conditional.resources inventory_resources.add_resource_gain(pickup_resources) resources_to_give.add_resource_gain(pickup_resources) resources_to_give.add_resource_gain( pickup.conversion_resource_gain(inventory_resources)) # Ignore item% for received items if self.game.resource_database.item_percentage is not None: resources_to_give.remove_resource( self.game.resource_database.item_percentage) return item_name, resources_to_give
def add_dark_temple_keys(resource_database: ResourceDatabase, ) -> PoolResults: """ :param resource_database: :return: """ item_pool: List[PickupEntry] = [] for temple_index in range(3): for i in range(3): item_pool.append( pickup_creator.create_dark_temple_key(i, temple_index, resource_database)) return PoolResults(item_pool, {}, ResourceCollection.with_database(resource_database))
def logic_bootstrap( self, configuration: BaseConfiguration, game: GameDescription, patches: GamePatches, ) -> Tuple[GameDescription, State]: """ Core code for starting a new Logic/State. :param configuration: :param game: :param patches: :return: """ if not game.mutable: raise ValueError("Running logic_bootstrap with non-mutable game") game.world_list.ensure_has_node_cache() starting_state = self.calculate_starting_state(game, patches, configuration) if configuration.trick_level.minimal_logic: self._add_minimal_logic_initial_resources( starting_state.resources, game, configuration.major_items_configuration) static_resources = ResourceCollection.with_database( game.resource_database) static_resources.add_resource_gain( self.trick_resources_for_configuration(configuration.trick_level, game.resource_database)) static_resources.add_resource_gain( self.event_resources_for_configuration(configuration, game.resource_database)) static_resources.add_resource_gain( self.version_resources_for_game(configuration, game.resource_database)) static_resources.add_resource_gain( self.misc_resources_for_configuration(configuration, game.resource_database)) for resource, quantity in static_resources.as_resource_gain(): starting_state.resources.set_resource(resource, quantity) game.patch_requirements(starting_state.resources, configuration.damage_strictness.value) return game, starting_state
def add_major_items( resource_database: ResourceDatabase, major_items_configuration: MajorItemsConfiguration, ammo_configuration: AmmoConfiguration, ) -> PoolResults: """ :param resource_database: :param major_items_configuration: :param ammo_configuration: :return: """ item_pool: List[PickupEntry] = [] new_assignment: Dict[PickupIndex, PickupEntry] = {} initial_resources = ResourceCollection.with_database(resource_database) for item, state in major_items_configuration.items_state.items(): if len(item.ammo_index) != len(state.included_ammo): raise InvalidConfiguration( "Item {0.name} uses {0.ammo_index} as ammo, but there's only {1} values in included_ammo" .format(item, len(state.included_ammo))) ammo, locked_ammo = _find_ammo_for(item.ammo_index, ammo_configuration) if state.include_copy_in_original_location: if item.original_index is None: raise InvalidConfiguration( "Item {0.name} does not exist in the original game, cannot use state {1}" .format(item, state), ) new_assignment[item.original_index] = create_major_item( item, state, True, resource_database, ammo, locked_ammo) for _ in range(state.num_shuffled_pickups): item_pool.append( create_major_item(item, state, True, resource_database, ammo, locked_ammo)) for _ in range(state.num_included_in_starting_items): initial_resources.add_resource_gain( create_major_item(item, state, False, resource_database, ammo, locked_ammo).all_resources, ) return PoolResults(item_pool, new_assignment, initial_resources)
def add_sky_temple_key_distribution_logic( resource_database: ResourceDatabase, mode: LayoutSkyTempleKeyMode, ) -> PoolResults: """ Adds the given Sky Temple Keys to the item pool :param resource_database: :param mode: :return: """ item_pool: List[PickupEntry] = [] new_assignment: Dict[PickupIndex, PickupEntry] = {} initial_resources = ResourceCollection.with_database(resource_database) if mode == LayoutSkyTempleKeyMode.ALL_BOSSES or mode == LayoutSkyTempleKeyMode.ALL_GUARDIANS: locations_to_place = _GUARDIAN_INDICES[:] if mode == LayoutSkyTempleKeyMode.ALL_BOSSES: locations_to_place += _SUB_GUARDIAN_INDICES for key_number, location in enumerate(locations_to_place): new_assignment[location] = create_sky_temple_key( key_number, resource_database) first_automatic_key = len(locations_to_place) else: keys_to_place = mode.value if not isinstance(keys_to_place, int): raise InvalidConfiguration( "Unknown Sky Temple Key mode: {}".format(mode)) for key_number in range(keys_to_place): item_pool.append( create_sky_temple_key(key_number, resource_database)) first_automatic_key = keys_to_place for automatic_key_number in range(first_automatic_key, 9): initial_resources.add_resource_gain( create_sky_temple_key(automatic_key_number, resource_database).all_resources, ) return PoolResults(item_pool, new_assignment, initial_resources)
def create_from_game( cls, game: GameDescription, player_index: int, configuration: BaseConfiguration, ) -> GamePatches: game.world_list.ensure_has_node_cache() return GamePatches( game, player_index, configuration, pickup_assignment={}, elevator_connection=game.get_default_elevator_connection(), dock_connection=[None] * len(game.world_list.all_nodes), dock_weakness=[None] * len(game.world_list.all_nodes), configurable_nodes={}, starting_items=ResourceCollection.with_database( game.resource_database), starting_location=game.starting_location, hints={}, )