Esempio n. 1
0
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)
Esempio n. 2
0
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
Esempio n. 3
0
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 == []
Esempio n. 5
0
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
Esempio n. 7
0
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))
Esempio n. 8
0
    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
Esempio n. 9
0
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)
Esempio n. 10
0
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)
Esempio n. 11
0
 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={},
     )