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)
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)
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)
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))