Example #1
0
def _area_name(world_list: WorldList, pickup_node: PickupNode,
               hide_world: bool) -> str:
    area = world_list.nodes_to_area(pickup_node)
    if hide_world:
        return area.name
    else:
        return world_list.area_name(area)
Example #2
0
    def add_relative_hint(self, world_list: WorldList,
                          patches: GamePatches,
                          rng: Random,
                          target: PickupIndex,
                          target_precision: HintItemPrecision,
                          relative_type: HintLocationPrecision,
                          precise_distance: bool,
                          precision: Union[HintItemPrecision, HintRelativeAreaName],
                          max_distance: int,
                          ) -> Optional[Hint]:
        """
        Creates a relative hint.
        :return: Might be None, if no hint could be created.
        """
        target_node = node_search.pickup_index_to_node(world_list, target)
        target_area = world_list.nodes_to_area(target_node)
        distances = node_search.distances_to_node(world_list, target_node, patches=patches, cutoff=max_distance)

        def _major_pickups(area: Area) -> Iterator[PickupIndex]:
            for index in area.pickup_indices:
                t = patches.pickup_assignment.get(index)
                # FIXME: None should be ok, but this must be called after junk has been filled
                if t is not None:
                    cat = t.pickup.item_category
                    if cat.is_major or (not cat.is_expansion and target_precision == HintItemPrecision.DETAILED):
                        yield index

        area_choices = {
            area: 1 / max(distance, 2)
            for area, distance in distances.items()
            if (distance > 0 and area.in_dark_aether == target_area.in_dark_aether
                and (relative_type == HintLocationPrecision.RELATIVE_TO_AREA or _not_empty(_major_pickups(area))))
        }
        if not area_choices:
            return None

        area = random_lib.select_element_with_weight(dict(sorted(area_choices.items(),
                                                                 key=lambda a: a[0].name)), rng)

        distance_offset = None
        if not precise_distance:
            distance_offset = max_distance - distances[area]

        if relative_type == HintLocationPrecision.RELATIVE_TO_AREA:
            relative = RelativeDataArea(distance_offset, world_list.identifier_for_area(area),
                                        precision)
        elif relative_type == HintLocationPrecision.RELATIVE_TO_INDEX:
            relative = RelativeDataItem(distance_offset, rng.choice(list(_major_pickups(area))), precision)
        else:
            raise ValueError(f"Invalid relative_type: {relative_type}")

        precision_pair = PrecisionPair(relative_type, target_precision, include_owner=False, relative=relative)
        return Hint(HintType.LOCATION, precision_pair, target)
Example #3
0
def distances_to_node(world_list: WorldList, starting_node: Node,
                      *,
                      ignore_elevators: bool = True,
                      cutoff: Optional[int] = None,
                      patches: Optional[GamePatches] = None,
                      ) -> Dict[Area, int]:
    """
    Compute the shortest distance from a node to all reachable areas.
    :param world_list:
    :param starting_node:
    :param ignore_elevators:
    :param cutoff: Exclude areas with a length longer that cutoff.
    :param patches:
    :return: Dict keyed by area to shortest distance to starting_node.
    """
    import networkx
    g = networkx.DiGraph()

    if patches is None:
        def get_elevator_connection_for(n: TeleporterNode):
            return n.default_connection

        def get_dock_connection_for(n: DockNode):
            return n.default_connection
    else:
        get_elevator_connection_for = patches.get_elevator_connection_for

        def get_dock_connection_for(n: DockNode):
            return patches.get_dock_connection_for(n).identifier

    for area in world_list.all_areas:
        g.add_node(area)

    for world in world_list.worlds:
        for area in world.areas:
            new_areas = set()
            for node in area.nodes:
                connection = None
                if isinstance(node, DockNode):
                    connection = get_dock_connection_for(node).area_identifier

                elif isinstance(node, TeleporterNode) and not ignore_elevators:
                    connection = get_elevator_connection_for(node)

                if connection is not None:
                    new_areas.add(world_list.area_by_area_location(connection))

            for next_area in new_areas:
                g.add_edge(area, next_area)

    return networkx.single_source_shortest_path_length(g, world_list.nodes_to_area(starting_node), cutoff)
Example #4
0
def _pretty_name_for_elevator(
    game: RandovaniaGame,
    world_list: WorldList,
    original_teleporter_node: TeleporterNode,
    connection: AreaIdentifier,
) -> str:
    """
    Calculates the name the room that contains this elevator should have
    :param world_list:
    :param original_teleporter_node:
    :param connection:
    :return:
    """
    if original_teleporter_node.keep_name_when_vanilla:
        if original_teleporter_node.default_connection == connection:
            return world_list.nodes_to_area(original_teleporter_node).name

    return "Transport to {}".format(
        elevators.get_elevator_or_area_name(game, world_list, connection,
                                            False))