async def test_receive_required_missile_launcher( connector: EchoesRemoteConnector, echoes_item_database, echoes_resource_database): pickup = pickup_creator.create_major_item( echoes_item_database.major_items["Missile Launcher"], MajorItemState(included_ammo=(5, )), True, echoes_resource_database, echoes_item_database.ammo["Missile Expansion"], True, ) executor = AsyncMock() permanent_pickups = (("Received Missile Launcher from Someone Else", pickup), ) inventory = { echoes_resource_database.multiworld_magic_item: InventoryItem(0, 0), } # Run patches, has_message = await connector.find_missing_remote_pickups( executor, inventory, permanent_pickups, False, ) assert has_message assert len(patches) == 5 await connector.execute_remote_patches(executor, patches)
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: CurrentResources = {} 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): add_resource_gain_to_current_resources( create_major_item(item, state, False, resource_database, ammo, locked_ammo).all_resources, initial_resources) return PoolResults(item_pool, new_assignment, initial_resources)
def test_pickup_scan_for_progressive_suit(echoes_item_database, echoes_resource_database): # Setup progressive_suit = echoes_item_database.major_items["Progressive Suit"] pickup = pickup_creator.create_major_item(progressive_suit, MajorItemState(), False, echoes_resource_database, None, False) # Run result = patcher_file._pickup_scan(pickup) # Assert assert result == "Progressive Suit. Provides the following in order: Dark Suit, Light Suit"
def test_get_single_hud_text_all_major_items(echoes_item_database, echoes_resource_database): memo_data = default_prime2_memo_data() # Run for item in echoes_item_database.major_items.values(): pickup = pickup_creator.create_major_item(item, MajorItemState(), False, echoes_resource_database, None, False) result = patcher_file._get_all_hud_text(pickup, memo_data) for i, progression in enumerate(pickup.resources): assert progression.name in result[i] assert result for line in result: assert len(line) > 10 assert isinstance(line, str)
def test_get_single_hud_text_all_major_items(echoes_item_database, echoes_resource_database): memo_data = default_database.default_prime2_memo_data() # Run for item in echoes_item_database.major_items.values(): pickup = pickup_creator.create_major_item(item, MajorItemState(), False, echoes_resource_database, None, False) result = pickup_exporter._get_all_hud_text(pickup_exporter._conditional_resources_for_pickup(pickup), memo_data) for i, progression in enumerate(pickup.progression): assert progression[0].long_name in result[i] assert result for line in result: assert len(line) > 10 assert isinstance(line, str)
def test_create_seeker_launcher( ammo_quantity: int, ammo_requires_major_item: bool, echoes_item_database, echoes_resource_database, ): # Setup missile = echoes_resource_database.get_item("Missile") missile_launcher = echoes_resource_database.get_item("MissileLauncher") seeker_launcher = echoes_resource_database.get_item("Seekers") temporary = echoes_resource_database.get_item("Temporary1") state = MajorItemState( include_copy_in_original_location=False, num_shuffled_pickups=0, num_included_in_starting_items=0, included_ammo=(ammo_quantity, ), ) # Run result = pickup_creator.create_major_item( echoes_item_database.major_items["Seeker Launcher"], state, True, echoes_resource_database, echoes_item_database.ammo["Missile Expansion"], ammo_requires_major_item) # Assert assert result == PickupEntry( name="Seeker Launcher", progression=((seeker_launcher, 1), ), extra_resources=( (missile, ammo_quantity), (echoes_resource_database.item_percentage, 1), ), model=PickupModel(echoes_resource_database.game_enum, "SeekerLauncher"), item_category=echoes_item_database.item_categories["missile"], broad_category=echoes_item_database.item_categories["missile_related"], respects_lock=ammo_requires_major_item, resource_lock=ResourceLock( locked_by=missile_launcher, temporary_item=temporary, item_to_lock=missile, ), )
def test_pickup_data_for_seeker_launcher(echoes_item_database, echoes_resource_database): # Setup state = MajorItemState( include_copy_in_original_location=False, num_shuffled_pickups=0, num_included_in_starting_items=0, included_ammo=(5,), ) pickup = pickup_creator.create_major_item( echoes_item_database.major_items["Seeker Launcher"], state, True, echoes_resource_database, echoes_item_database.ammo["Missile Expansion"], True ) creator = pickup_exporter.PickupExporterSolo(patch_data_factory._simplified_memo_data()) # Run details = creator.export(PickupIndex(0), PickupTarget(pickup, 0), pickup, PickupModelStyle.ALL_VISIBLE) result = patch_data_factory.echoes_pickup_details_to_patcher(details, MagicMock()) # Assert assert result == { "pickup_index": 0, "scan": "Seeker Launcher", "model": {"game": "prime2", "name": "SeekerLauncher"}, "hud_text": ["Seeker Launcher acquired, but the Missile Launcher is required to use it.", "Seeker Launcher acquired!"], 'resources': [{'amount': 5, 'index': 71}, {'amount': 1, 'index': 47}, {'amount': 1, 'index': 26}], "conditional_resources": [ {'item': 73, 'resources': [{'amount': 5, 'index': 44}, {'amount': 1, 'index': 47}, {'amount': 1, 'index': 26}]} ], "convert": [], }
def test_missile_expansion_before_launcher(include_before, echoes_item_database, echoes_resource_database): # Setup ammo = echoes_item_database.ammo["Missile Expansion"] major_item = echoes_item_database.major_items["Missile Launcher"] missile = echoes_resource_database.get_item(ammo.items[0]) missile_launcher = echoes_resource_database.get_item(major_item.progression[0]) temporary = echoes_resource_database.get_item(ammo.temporary) percent = echoes_resource_database.item_percentage # Run expansion = pickup_creator.create_ammo_expansion(ammo, [5], True, echoes_resource_database) launcher = pickup_creator.create_major_item( major_item, MajorItemState(included_ammo=(5,)), True, echoes_resource_database, ammo, True ) resources = {} if include_before: # Ammo Expansion add_resource_gain_to_current_resources(expansion.resource_gain(resources, force_lock=True), resources) assert resources == {percent: 1, temporary: 5} # Add Launcher add_resource_gain_to_current_resources(launcher.resource_gain(resources, force_lock=True), resources) if include_before: assert resources == {percent: 2, temporary: 0, missile_launcher: 1, missile: 10} else: assert resources == {percent: 1, temporary: 0, missile_launcher: 1, missile: 5} # Ammo Expansion add_resource_gain_to_current_resources(expansion.resource_gain(resources, force_lock=True), resources) if include_before: assert resources == {percent: 3, temporary: 0, missile_launcher: 1, missile: 15} else: assert resources == {percent: 2, temporary: 0, missile_launcher: 1, missile: 10}
def _patches_with_data(request, echoes_game_data, echoes_item_database): game = data_reader.decode_data(echoes_game_data) data = { "starting_location": "Temple Grounds/Landing Site", "starting_items": {}, "elevators": { "Temple Grounds/Temple Transport C": "Great Temple/Temple Transport C", "Temple Grounds/Transport to Agon Wastes": "Agon Wastes/Transport to Temple Grounds", "Temple Grounds/Transport to Torvus Bog": "Torvus Bog/Transport to Temple Grounds", "Temple Grounds/Temple Transport B": "Great Temple/Temple Transport B", "Temple Grounds/Transport to Sanctuary Fortress": "Sanctuary Fortress/Transport to Temple Grounds", "Temple Grounds/Temple Transport A": "Great Temple/Temple Transport A", "Great Temple/Temple Transport A": "Temple Grounds/Temple Transport A", "Great Temple/Temple Transport C": "Temple Grounds/Temple Transport C", "Great Temple/Temple Transport B": "Temple Grounds/Temple Transport B", "Sky Temple Grounds/Sky Temple Gateway": "Sky Temple/Sky Temple Energy Controller", "Sky Temple/Sky Temple Energy Controller": "Sky Temple Grounds/Sky Temple Gateway", "Agon Wastes/Transport to Temple Grounds": "Temple Grounds/Transport to Agon Wastes", "Agon Wastes/Transport to Torvus Bog": "Torvus Bog/Transport to Agon Wastes", "Agon Wastes/Transport to Sanctuary Fortress": "Sanctuary Fortress/Transport to Agon Wastes", "Torvus Bog/Transport to Temple Grounds": "Temple Grounds/Transport to Torvus Bog", "Torvus Bog/Transport to Agon Wastes": "Agon Wastes/Transport to Torvus Bog", "Torvus Bog/Transport to Sanctuary Fortress": "Sanctuary Fortress/Transport to Torvus Bog", "Sanctuary Fortress/Transport to Temple Grounds": "Temple Grounds/Transport to Sanctuary Fortress", "Sanctuary Fortress/Transport to Agon Wastes": "Agon Wastes/Transport to Sanctuary Fortress", "Sanctuary Fortress/Transport to Torvus Bog": "Torvus Bog/Transport to Sanctuary Fortress", "Sanctuary Fortress/Aerie": "Sanctuary Fortress/Aerie Transport Station", "Sanctuary Fortress/Aerie Transport Station": "Sanctuary Fortress/Aerie", }, "translators": {}, "locations": {}, "hints": {}, "_locations_internal": "", } patches = game.create_game_patches() locations = collections.defaultdict(dict) for world, area, node in game.world_list.all_worlds_areas_nodes: if node.is_resource_node and isinstance(node, PickupNode): world_name = world.dark_name if area.in_dark_aether else world.name locations[world_name][game.world_list.node_name(node)] = game_patches_serializer._ETM_NAME data["locations"] = { world: { area: item for area, item in sorted(locations[world].items()) } for world in sorted(locations.keys()) } if request.param.get("starting_item"): item_name = request.param.get("starting_item") patches = patches.assign_extra_initial_items({ find_resource_info_with_long_name(game.resource_database.item, item_name): 1, }) data["starting_items"][item_name] = 1 if request.param.get("elevator"): elevator_id, elevator_source = request.param.get("elevator") elevator_connection = copy.copy(patches.elevator_connection) elevator_connection[elevator_id] = game.starting_location patches = dataclasses.replace(patches, elevator_connection=elevator_connection) data["elevators"][elevator_source] = "Temple Grounds/Landing Site" if request.param.get("translator"): gates = {} for index, gate_name, translator in request.param.get("translator"): gates[TranslatorGate(index)] = find_resource_info_with_long_name(game.resource_database.item, translator) data["translators"][gate_name] = translator patches = patches.assign_gate_assignment(gates) if request.param.get("pickup"): data["_locations_internal"], pickup_name = request.param.get("pickup") pickup = pickup_creator.create_major_item(echoes_item_database.major_items[pickup_name], MajorItemState(), True, game.resource_database, None, False) patches = patches.assign_new_pickups([(PickupIndex(5), pickup)]) data["locations"]["Temple Grounds"]['Transport to Agon Wastes/Pickup (Missile)'] = pickup_name if request.param.get("hint"): asset, hint = request.param.get("hint") patches = patches.assign_hint(LogbookAsset(asset), Hint.from_json(hint)) data["hints"][str(asset)] = hint return data, patches
def test_create_pickup_for(percentage: bool, echoes_item_database, echoes_resource_database, generic_item_category): # Setup item_a = echoes_resource_database.get_item("DarkVisor") item_b = echoes_resource_database.get_item("MorphBall") item_c = echoes_resource_database.get_item("Bombs") ammo_a = echoes_resource_database.get_item("EnergyTank") ammo_b = echoes_resource_database.get_item("DarkAmmo") less_generic_item_category = ItemCategory(name="the_category", long_name="The Category", hint_details=("a ", " wonderful item"), is_major=True) major_item = MajorItem( game=echoes_resource_database.game_enum, name="The Item", item_category=less_generic_item_category, broad_category=generic_item_category, model_name="SuperModel", progression=("DarkVisor", "MorphBall", "Bombs"), default_starting_count=0, default_shuffled_count=1, ammo_index=("EnergyTank", "DarkAmmo"), must_be_starting=False, original_index=None, probability_offset=5, ) state = MajorItemState( include_copy_in_original_location=False, num_shuffled_pickups=0, num_included_in_starting_items=0, included_ammo=(10, 20), ) if percentage: extra_resources = ( (ammo_a, 10), (ammo_b, 20), (echoes_resource_database.item_percentage, 1), ) else: extra_resources = ( (ammo_a, 10), (ammo_b, 20), ) # Run result = pickup_creator.create_major_item(major_item, state, percentage, echoes_resource_database, None, False) # Assert assert result == PickupEntry( name="The Item", model=PickupModel(echoes_resource_database.game_enum, "SuperModel"), progression=( (item_a, 1), (item_b, 1), (item_c, 1), ), extra_resources=extra_resources, item_category=less_generic_item_category, broad_category=generic_item_category, probability_offset=5, respects_lock=False, )
def _patches_with_data(request, echoes_game_description, echoes_game_patches, echoes_item_database): game = echoes_game_description db = game.resource_database data = { "game": echoes_game_description.game.value, "starting_location": "Temple Grounds/Landing Site", "starting_items": {}, "teleporters": { "Temple Grounds/Temple Transport C/Elevator to Great Temple - Temple Transport C": "Great Temple/Temple Transport C", "Temple Grounds/Transport to Agon Wastes/Elevator to Agon Wastes - Transport to Temple Grounds": "Agon Wastes/Transport to Temple Grounds", "Temple Grounds/Transport to Torvus Bog/Elevator to Torvus Bog - Transport to Temple Grounds": "Torvus Bog/Transport to Temple Grounds", "Temple Grounds/Temple Transport B/Elevator to Great Temple - Temple Transport B": "Great Temple/Temple Transport B", "Temple Grounds/Transport to Sanctuary Fortress/Elevator to Sanctuary Fortress - Transport to Temple Grounds": "Sanctuary Fortress/Transport to Temple Grounds", "Temple Grounds/Temple Transport A/Elevator to Great Temple - Temple Transport A": "Great Temple/Temple Transport A", "Great Temple/Temple Transport A/Elevator to Temple Grounds - Temple Transport A": "Temple Grounds/Temple Transport A", "Great Temple/Temple Transport C/Elevator to Temple Grounds - Temple Transport C": "Temple Grounds/Temple Transport C", "Great Temple/Temple Transport B/Elevator to Temple Grounds - Temple Transport B": "Temple Grounds/Temple Transport B", "Temple Grounds/Sky Temple Gateway/Teleport to Great Temple - Sky Temple Energy Controller": "Great Temple/Sky Temple Energy Controller", "Great Temple/Sky Temple Energy Controller/Teleport to Temple Grounds - Sky Temple Gateway": "Temple Grounds/Sky Temple Gateway", "Agon Wastes/Transport to Temple Grounds/Elevator to Temple Grounds - Transport to Agon Wastes": "Temple Grounds/Transport to Agon Wastes", "Agon Wastes/Transport to Torvus Bog/Elevator to Torvus Bog - Transport to Agon Wastes": "Torvus Bog/Transport to Agon Wastes", "Agon Wastes/Transport to Sanctuary Fortress/Elevator to Sanctuary Fortress - Transport to Agon Wastes": "Sanctuary Fortress/Transport to Agon Wastes", "Torvus Bog/Transport to Temple Grounds/Elevator to Temple Grounds - Transport to Torvus Bog": "Temple Grounds/Transport to Torvus Bog", "Torvus Bog/Transport to Agon Wastes/Elevator to Agon Wastes - Transport to Torvus Bog": "Agon Wastes/Transport to Torvus Bog", "Torvus Bog/Transport to Sanctuary Fortress/Elevator to Sanctuary Fortress - Transport to Torvus Bog": "Sanctuary Fortress/Transport to Torvus Bog", "Sanctuary Fortress/Transport to Temple Grounds/Elevator to Temple Grounds - Transport to Sanctuary Fortress": "Temple Grounds/Transport to Sanctuary Fortress", "Sanctuary Fortress/Transport to Agon Wastes/Elevator to Agon Wastes - Transport to Sanctuary Fortress": "Agon Wastes/Transport to Sanctuary Fortress", "Sanctuary Fortress/Transport to Torvus Bog/Elevator to Torvus Bog - Transport to Sanctuary Fortress": "Torvus Bog/Transport to Sanctuary Fortress", "Sanctuary Fortress/Aerie/Elevator to Sanctuary Fortress - Aerie Transport Station": "Sanctuary Fortress/Aerie Transport Station", "Sanctuary Fortress/Aerie Transport Station/Elevator to Sanctuary Fortress - Aerie": "Sanctuary Fortress/Aerie", }, "dock_weakness": {}, "configurable_nodes": {}, "locations": {}, "hints": {} } patches = dataclasses.replace(echoes_game_patches, player_index=0) locations = collections.defaultdict(dict) for world, area, node in game.world_list.all_worlds_areas_nodes: if node.is_resource_node and isinstance(node, PickupNode): world_name = world.dark_name if area.in_dark_aether else world.name locations[world_name][game.world_list.node_name( node)] = game_patches_serializer._ETM_NAME data["locations"] = { world: {area: item for area, item in sorted(locations[world].items())} for world in sorted(locations.keys()) } if request.param.get("starting_item"): item_name = request.param.get("starting_item") patches = patches.assign_extra_initial_items([ (db.get_item_by_name(item_name), 1), ]) data["starting_items"][item_name] = 1 if request.param.get("elevator"): teleporter: NodeIdentifier = request.param.get("elevator") patches = patches.assign_elevators([ (game.world_list.get_teleporter_node(teleporter), game.starting_location), ]) data["teleporters"][ teleporter.as_string] = "Temple Grounds/Landing Site" if request.param.get("configurable_nodes"): gates = [] for identifier, translator in request.param.get("configurable_nodes"): requirement = ResourceRequirement.simple(db.get_item(translator)) gates.append((NodeIdentifier.from_string(identifier), requirement)) data["configurable_nodes"][ identifier] = data_writer.write_requirement(requirement) patches = patches.assign_node_configuration(gates) if request.param.get("pickup"): pickup_name = request.param.get("pickup") pickup = pickup_creator.create_major_item( echoes_item_database.major_items[pickup_name], MajorItemState(), True, game.resource_database, None, False) patches = patches.assign_new_pickups([(PickupIndex(5), PickupTarget(pickup, 0))]) data["locations"]["Temple Grounds"][ 'Transport to Agon Wastes/Pickup (Missile)'] = pickup_name if request.param.get("hint"): identifier, hint = request.param.get("hint") patches = patches.assign_hint(NodeIdentifier.from_string(identifier), Hint.from_json(hint)) data["hints"][identifier] = hint return data, patches
def _patches_with_data(request, echoes_game_data, echoes_item_database): game = data_reader.decode_data(echoes_game_data) data = { "starting_location": "Temple Grounds/Landing Site", "starting_items": {}, "elevators": { "Temple Grounds/Temple Transport C": "Great Temple/Temple Transport C", "Temple Grounds/Transport to Agon Wastes": "Agon Wastes/Transport to Temple Grounds", "Temple Grounds/Transport to Torvus Bog": "Torvus Bog/Transport to Temple Grounds", "Temple Grounds/Temple Transport B": "Great Temple/Temple Transport B", "Temple Grounds/Sky Temple Gateway": "Great Temple/Sky Temple Energy Controller", "Temple Grounds/Transport to Sanctuary Fortress": "Sanctuary Fortress/Transport to Temple Grounds", "Temple Grounds/Temple Transport A": "Great Temple/Temple Transport A", "Great Temple/Temple Transport A": "Temple Grounds/Temple Transport A", "Great Temple/Temple Transport C": "Temple Grounds/Temple Transport C", "Great Temple/Temple Transport B": "Temple Grounds/Temple Transport B", "Great Temple/Sky Temple Energy Controller": "Temple Grounds/Sky Temple Gateway", "Agon Wastes/Transport to Temple Grounds": "Temple Grounds/Transport to Agon Wastes", "Agon Wastes/Transport to Torvus Bog": "Torvus Bog/Transport to Agon Wastes", "Agon Wastes/Transport to Sanctuary Fortress": "Sanctuary Fortress/Transport to Agon Wastes", "Torvus Bog/Transport to Temple Grounds": "Temple Grounds/Transport to Torvus Bog", "Torvus Bog/Transport to Agon Wastes": "Agon Wastes/Transport to Torvus Bog", "Torvus Bog/Transport to Sanctuary Fortress": "Sanctuary Fortress/Transport to Torvus Bog", "Sanctuary Fortress/Transport to Temple Grounds": "Temple Grounds/Transport to Sanctuary Fortress", "Sanctuary Fortress/Transport to Agon Wastes": "Agon Wastes/Transport to Sanctuary Fortress", "Sanctuary Fortress/Transport to Torvus Bog": "Torvus Bog/Transport to Sanctuary Fortress" }, "translators": {}, "locations": { world.name: { game.world_list.node_name(node): "Nothing" for node in world.all_nodes if node.is_resource_node and isinstance(node, PickupNode) } for world in sorted(game.world_list.worlds, key=lambda w: w.name) }, "hints": {}, "_locations_internal": "", } patches = GamePatches.with_game(game) if request.param.get("starting_item"): item_name = request.param.get("starting_item") patches = patches.assign_extra_initial_items({ find_resource_info_with_long_name(game.resource_database.item, item_name): 1, }) data["starting_items"][item_name] = 1 if request.param.get("elevator"): elevator_id, elevator_source = request.param.get("elevator") elevator_connection = copy.copy(patches.elevator_connection) elevator_connection[elevator_id] = game.starting_location patches = dataclasses.replace(patches, elevator_connection=elevator_connection) data["elevators"][elevator_source] = "Temple Grounds/Landing Site" if request.param.get("translator"): gates = {} for index, gate_name, translator in request.param.get("translator"): gates[TranslatorGate(index)] = find_resource_info_with_long_name( game.resource_database.item, translator) data["translators"][gate_name] = translator patches = patches.assign_gate_assignment(gates) if request.param.get("pickup"): data["_locations_internal"], pickup_name = request.param.get("pickup") pickup = pickup_creator.create_major_item( echoes_item_database.major_items[pickup_name], MajorItemState(), True, game.resource_database, None, False) patches = patches.assign_new_pickups([(PickupIndex(5), pickup)]) data["locations"]["Temple Grounds"][ 'Transport to Agon Wastes/Pickup (Missile)'] = pickup_name if request.param.get("hint"): asset, hint = request.param.get("hint") patches = patches.assign_hint(LogbookAsset(asset), Hint.from_json(hint)) data["hints"][str(asset)] = hint return data, patches