def test_create_message_for_hint_relative_area(echoes_game_description, blank_pickup, players_config, offset, distance_text): hint_name_creator = LocationHintCreator(echoes_game_description.world_list, None, None) world_list = echoes_game_description.world_list patches = echoes_game_description.create_game_patches( ).assign_pickup_assignment({ PickupIndex(5): PickupTarget(blank_pickup, 0), }) hint_name_creator = LocationHintCreator(world_list, None, None) location_formatters = { HintLocationPrecision.RELATIVE_TO_AREA: RelativeAreaFormatter(world_list, patches) } hint = Hint( HintType.LOCATION, PrecisionPair(HintLocationPrecision.RELATIVE_TO_AREA, HintItemPrecision.DETAILED, include_owner=False, relative=RelativeDataArea( offset, AreaLocation(1039999561, 3822429534), HintRelativeAreaName.NAME)), PickupIndex(5)) # Run result = hint_name_creator.create_message_for_hint(hint, {0: patches}, players_config, location_formatters) # Assert assert result == ( f'The &push;&main-color=#FF6705B3;Blank Pickup&pop; can be found ' f'&push;&main-color=#FF3333;{distance_text} {10 + (offset or 0)} rooms&pop; away from ' f'Torvus Bog - Great Bridge.')
def test_create_hints_item_dark_temple_keys(empty_patches, players_config, echoes_game_description, blank_pickup, indices, expected_message): # Setup hint_name_creator = LocationHintCreator(echoes_game_description.world_list, None, None) db = echoes_game_description.resource_database keys = [ (PickupIndex(index), dataclasses.replace(blank_pickup, progression=((db.get_item(item), 1), ))) for index, item in zip(indices, echoes_items.DARK_TEMPLE_KEY_ITEMS[1]) ] patches = dataclasses.replace(empty_patches, pickup_assignment={ pickup_index: PickupTarget(key, 0) for pickup_index, key in keys }) hint = Hint(HintType.RED_TEMPLE_KEY_SET, None, dark_temple=HintDarkTemple.TORVUS_BOG) # Run result = hint_name_creator.create_message_for_hint(hint, {0: patches}, players_config, {}) # Assert assert result == expected_message
def create_hints( all_patches: Dict[int, GamePatches], players_config: PlayersConfiguration, world_list: WorldList, rng: Random, ) -> list: """ Creates the string patches entries that changes the Lore scans in the game for item pickups :param all_patches: :param players_config: :param world_list: :param rng: :return: """ hint_name_creator = LocationHintCreator(world_list, rng, _JOKE_HINTS) patches = all_patches[players_config.player_index] location_formatters: Dict[HintLocationPrecision, LocationFormatter] = { HintLocationPrecision.KEYBEARER: TemplatedFormatter( "The Flying Ing Cache in {node} contains {determiner}{pickup}.", hint_name_creator), HintLocationPrecision.GUARDIAN: GuardianFormatter(), HintLocationPrecision.LIGHT_SUIT_LOCATION: TemplatedFormatter( "U-Mos's reward for returning the Sanctuary energy is {determiner}{pickup}.", hint_name_creator), HintLocationPrecision.DETAILED: TemplatedFormatter( "{determiner.title}{pickup} can be found in {node}.", hint_name_creator), HintLocationPrecision.WORLD_ONLY: TemplatedFormatter( "{determiner.title}{pickup} can be found in {node}.", hint_name_creator), HintLocationPrecision.RELATIVE_TO_AREA: RelativeAreaFormatter(world_list, patches), HintLocationPrecision.RELATIVE_TO_INDEX: RelativeItemFormatter(world_list, patches, players_config), } hints_for_asset: Dict[int, str] = {} for asset, hint in patches.hints.items(): hints_for_asset[ asset.asset_id] = hint_name_creator.create_message_for_hint( hint, all_patches, players_config, location_formatters) return [ create_simple_logbook_hint( logbook_node.string_asset_id, hints_for_asset.get(logbook_node.string_asset_id, "Someone forgot to leave a message."), ) for logbook_node in world_list.all_nodes if isinstance(logbook_node, LogbookNode) ]
def create_message_for_hint( hint: Hint, all_patches: Dict[int, GamePatches], players_config: PlayersConfiguration, hint_name_creator: LocationHintCreator, location_formatters: Dict[HintLocationPrecision, LocationFormatter], world_list: WorldList, ) -> str: if hint.hint_type == HintType.JOKE: return color_text(TextColor.JOKE, hint_name_creator.create_joke_hint()) elif hint.hint_type == HintType.RED_TEMPLE_KEY_SET: return create_temple_key_hint(all_patches, players_config.player_index, hint.dark_temple, world_list) else: assert hint.hint_type == HintType.LOCATION patches = all_patches[players_config.player_index] determiner, pickup_name = _calculate_pickup_hint( patches.pickup_assignment, world_list, hint.precision.item, patches.pickup_assignment.get(hint.target)) return location_formatters[hint.precision.location].format( Determiner(determiner), color_text(TextColor.ITEM, pickup_name), hint, )
def create_message_for_hint( hint: Hint, all_patches: Dict[int, GamePatches], players_config: PlayersConfiguration, hint_name_creator: LocationHintCreator, location_formatters: Dict[HintLocationPrecision, LocationFormatter], world_list: WorldList, ) -> str: if hint.hint_type == HintType.JOKE: return color_text(TextColor.JOKE, hint_name_creator.create_joke_hint()) elif hint.hint_type == HintType.RED_TEMPLE_KEY_SET: return create_temple_key_hint(all_patches, players_config.player_index, hint.dark_temple, world_list) else: assert hint.hint_type == HintType.LOCATION patches = all_patches[players_config.player_index] pickup_target = patches.pickup_assignment.get(hint.target) determiner, pickup_name = _calculate_pickup_hint( patches.pickup_assignment, world_list, hint.precision.item, pickup_target, players_config) use_title_formatting = True if hint.precision.include_owner and len( players_config.player_names) > 1: target_player = pickup_target.player if pickup_target is not None else players_config.player_index determiner = f"{players_config.player_names[target_player]}'s " use_title_formatting = False return location_formatters[hint.precision.location].format( Determiner(determiner, use_title_formatting), color_text(TextColor.ITEM, pickup_name), hint, )
def test_create_message_for_hint_dark_temple_no_keys(empty_patches, players_config, echoes_game_description): # Setup hint_name_creator = LocationHintCreator(echoes_game_description.world_list, None, None) hint = Hint(HintType.RED_TEMPLE_KEY_SET, None, dark_temple=HintDarkTemple.TORVUS_BOG) # Run result = hint_name_creator.create_message_for_hint(hint, {0: empty_patches}, players_config, {}) # Assert assert result == 'The keys to &push;&main-color=#FF6705B3;Dark Torvus Temple&pop; are nowhere to be found.'
def test_create_message_for_hint_relative_item(echoes_game_description, blank_pickup, players_config, distance_precise, distance_text, reference_precision, reference_name): world_list = echoes_game_description.world_list patches = echoes_game_description.create_game_patches( ).assign_pickup_assignment({ PickupIndex(5): PickupTarget(blank_pickup, 0), PickupIndex(15): PickupTarget( dataclasses.replace(blank_pickup, name="Reference Pickup"), 0), }) hint_name_creator = LocationHintCreator(world_list, None, None) location_formatters = { HintLocationPrecision.RELATIVE_TO_INDEX: RelativeItemFormatter(world_list, patches, players_config), } hint = Hint( HintType.LOCATION, PrecisionPair(HintLocationPrecision.RELATIVE_TO_INDEX, HintItemPrecision.DETAILED, include_owner=False, relative=RelativeDataItem(distance_precise, PickupIndex(15), reference_precision)), PickupIndex(5)) # Run result = hint_name_creator.create_message_for_hint(hint, {0: patches}, players_config, location_formatters) # Assert assert result == ( f'The &push;&main-color=#FF6705B3;Blank Pickup&pop; can be found ' f'&push;&main-color=#FF3333;{distance_text} {7 + (distance_precise or 0)} ' f'rooms&pop; away from {reference_name}.')
def create_hints( patches: GamePatches, world_list: WorldList, hide_area: bool, ) -> list: """ Creates the string patches entries that changes the Sky Temple Gateway hint scans with hints for where the STK actually are. :param patches: :param world_list: :param hide_area: Should the hint include only the world? :return: """ location_hint_creator = LocationHintCreator(world_list) sky_temple_key_hints = {} for pickup_index, pickup in patches.pickup_assignment.items(): resources = resource_info.convert_resource_gain_to_current_resources( pickup.resource_gain({})) for resource, quantity in resources.items(): if quantity < 1: continue try: key_number = echoes_items.SKY_TEMPLE_KEY_ITEMS.index( resource.index) + 1 except ValueError: continue assert resource.index not in sky_temple_key_hints sky_temple_key_hints[ resource.index] = "{} is located in {}.".format( _sky_temple_key_name(key_number), color_text( TextColor.LOCATION, location_hint_creator.index_node_name( pickup_index, hide_area), ), ) for starting_resource, quantity in patches.starting_items.items(): if quantity < 1: continue try: key_number = echoes_items.SKY_TEMPLE_KEY_ITEMS.index( starting_resource.index) + 1 except ValueError: continue assert starting_resource.index not in sky_temple_key_hints sky_temple_key_hints[ starting_resource.index] = "{} has no need to be located.".format( _sky_temple_key_name(key_number), ) if len(sky_temple_key_hints) != len(echoes_items.SKY_TEMPLE_KEY_ITEMS): raise ValueError( "Expected to find {} Sky Temple Keys between pickup placement and starting items, found {}" .format(len(echoes_items.SKY_TEMPLE_KEY_ITEMS), len(sky_temple_key_hints))) return [ create_simple_logbook_hint(_SKY_TEMPLE_KEY_SCAN_ASSETS[key_number], sky_temple_key_hints[key_index]) for key_number, key_index in enumerate(echoes_items.SKY_TEMPLE_KEY_ITEMS) ]
def create_hints(patches: GamePatches, world_list: WorldList, rng: Random, ) -> list: """ Creates the string patches entries that changes the Lore scans in the game for item pickups :param patches: :param world_list: :param rng: :return: """ hint_name_creator = LocationHintCreator(world_list) joke_items = sorted(set(_PRIME_1_ITEMS) | set(_PRIME_3_ITEMS)) joke_locations = sorted(set(_PRIME_1_LOCATIONS) | set(_PRIME_3_LOCATIONS)) joke_hints = sorted(_JOKE_HINTS) rng.shuffle(joke_items) rng.shuffle(joke_locations) rng.shuffle(joke_hints) hints_for_asset: Dict[int, str] = {} for asset, hint in patches.hints.items(): if hint.precision.is_joke: if not joke_hints: joke_hints = sorted(_JOKE_HINTS) rng.shuffle(joke_hints) message = color_text(TextColor.JOKE, joke_hints.pop()) else: target = patches.pickup_assignment.get(hint.target) # Determine location name if hint.hint_type is HintType.GUARDIAN: node_name = color_text(TextColor.GUARDIAN, _GUARDIAN_NAMES[hint.target]) elif hint.location_precision == HintLocationPrecision.WRONG_GAME: node_name = color_text(TextColor.JOKE, "{} (?)".format(joke_locations.pop()) if joke_locations else "an unknown location") else: node_name = color_text(TextColor.LOCATION, hint_name_creator.index_node_name( hint.target, hint.location_precision != HintLocationPrecision.DETAILED )) # Determine pickup name if target is not None: is_joke, determiner, pickup_name = _calculate_pickup_hint( hint.item_precision, _calculate_determiner(patches.pickup_assignment, target.pickup), target.pickup, joke_items, ) else: is_joke = False determiner = "the " if len(patches.pickup_assignment) == 118 else "an " pickup_name = "Energy Transfer Module" if hint.hint_type is HintType.LOCATION: determiner = determiner.title() pickup_name = color_text(TextColor.JOKE if is_joke else TextColor.ITEM, pickup_name) message = _HINT_MESSAGE_TEMPLATES[hint.hint_type].format(determiner=determiner, pickup=pickup_name, node=node_name) hints_for_asset[asset.asset_id] = message return [ create_simple_logbook_hint( logbook_node.string_asset_id, hints_for_asset.get(logbook_node.string_asset_id, "Someone forgot to leave a message."), ) for logbook_node in world_list.all_nodes if isinstance(logbook_node, LogbookNode) ]
def test_create_hints_all_placed(hide_area: bool, multiworld: bool, empty_patches, echoes_game_description): # Setup hint_creator = LocationHintCreator(echoes_game_description.world_list, None, None) players_config = PlayersConfiguration(0, { 0: "you", 1: "them" } if multiworld else {0: "you"}) patches = empty_patches.assign_new_pickups([ (PickupIndex(17 + key), PickupTarget( pickup_creator.create_sky_temple_key( key, echoes_game_description.resource_database), 0)) for key in range(5 if multiworld else 9) ]) other_patches = empty_patches.assign_new_pickups([ (PickupIndex(17 + key), PickupTarget( pickup_creator.create_sky_temple_key( key, echoes_game_description.resource_database), 0)) for key in range(5, 9) ]) expected = [ { "asset_id": 0xD97685FE, "strings": _create_hint_text(hide_area, multiworld, 1, "Sky Temple Grounds - Profane Path") }, { "asset_id": 0x32413EFD, "strings": _create_hint_text(hide_area, multiworld, 2, "Sky Temple Grounds - Phazon Grounds") }, { "asset_id": 0xDD8355C3, "strings": _create_hint_text(hide_area, multiworld, 3, "Sky Temple Grounds - Ing Reliquary") }, { "asset_id": 0x3F5F4EBA, "strings": _create_hint_text(hide_area, multiworld, 4, "Great Temple - Transport A Access") }, { "asset_id": 0xD09D2584, "strings": _create_hint_text(hide_area, multiworld, 5, "Great Temple - Temple Sanctuary") }, { "asset_id": 0x3BAA9E87, "strings": _create_hint_text(hide_area, multiworld, 6, "Great Temple - Transport B Access") }, { "asset_id": 0xD468F5B9, "strings": _create_hint_text(hide_area, multiworld, 7, "Great Temple - Main Energy Controller") }, { "asset_id": 0x2563AE34, "strings": _create_hint_text(hide_area, multiworld, 8, "Great Temple - Main Energy Controller") }, { "asset_id": 0xCAA1C50A, "strings": _create_hint_text(hide_area, multiworld, 9, "Agon Wastes - Mining Plaza") }, ] # Run result = sky_temple_key_hint.create_hints( { 0: patches, 1: other_patches } if multiworld else {0: patches}, players_config, echoes_game_description.resource_database, { 0: hint_creator, 1: hint_creator } if multiworld else {0: hint_creator}, hide_area) # Assert assert result == expected