def bit_pack_unpack(cls, decoder: BitPackDecoder, name: str, database: ResourceDatabase) -> PickupEntry: model_index = decoder.decode_single(255) probability_offset = BitPackFloat.bit_pack_unpack( decoder, _PROBABILITY_OFFSET_META) probability_multiplier = BitPackFloat.bit_pack_unpack( decoder, _PROBABILITY_MULTIPLIER_META) item_category = ItemCategory.bit_pack_unpack(decoder, {}) broad_category = ItemCategory.bit_pack_unpack(decoder, {}) has_name = bitpacking.decode_bool(decoder) num_conditional = decoder.decode_single( MAXIMUM_PICKUP_CONDITIONAL_RESOURCES) + 1 conditional_resources = [] for i in range(num_conditional): item_name = None # TODO: get the first resource name if i > 0: item_dependency = decoder.decode_element(database.item) else: item_dependency = None resources = [] for _ in range(decoder.decode_single(MAXIMUM_PICKUP_RESOURCES + 1)): resource = decoder.decode_element(database.item) quantity = decoder.decode_single(255) resources.append((resource, quantity)) if has_name: if bitpacking.decode_bool(decoder): item_name = name else: item_name = resources[0][0].long_name conditional_resources.append( ConditionalResources( name=item_name, item=item_dependency, resources=tuple(resources), )) num_convert = decoder.decode_single(MAXIMUM_PICKUP_CONVERSION + 1) convert_resources = [] for i in range(num_convert): source = decoder.decode_element(database.item) target = decoder.decode_element(database.item) convert_resources.append(ResourceConversion(source, target)) return PickupEntry( name=name, model_index=model_index, item_category=item_category, broad_category=broad_category, resources=tuple(conditional_resources), convert_resources=tuple(convert_resources), probability_offset=probability_offset, probability_multiplier=probability_multiplier, )
def test_bit_pack_pickup_entry(has_convert: bool, echoes_resource_database): # Setup name = "Some Random Name" if has_convert: convert_resources = (ResourceConversion( find_resource_info_with_long_name(echoes_resource_database.item, "Morph Ball"), find_resource_info_with_long_name(echoes_resource_database.item, "Item Percentage")), ) else: convert_resources = () pickup = PickupEntry( name=name, model_index=26, item_category=ItemCategory.TEMPLE_KEY, broad_category=ItemCategory.KEY, resources=(ConditionalResources( "Morph Ball", None, ( (find_resource_info_with_long_name( echoes_resource_database.item, "Morph Ball"), 2), (find_resource_info_with_long_name( echoes_resource_database.item, "Item Percentage"), 5), ), ), ConditionalResources( "Grapple Beam", find_resource_info_with_long_name( echoes_resource_database.item, "Morph Ball"), ((find_resource_info_with_long_name( echoes_resource_database.item, "Grapple Beam"), 3), ), )), convert_resources=convert_resources) # Run encoded = bitpacking.pack_value( BitPackPickupEntry(pickup, echoes_resource_database)) decoder = BitPackDecoder(encoded) decoded = BitPackPickupEntry.bit_pack_unpack(decoder, name, echoes_resource_database) # Assert assert pickup == decoded
def test_add_resource_gain_to_current_resources_convert(): # Setup resource_a = SimpleResourceInfo(1, "A", "A", ResourceType.ITEM) resource_b = SimpleResourceInfo(2, "B", "B", ResourceType.ITEM) pickup = PickupEntry(name="P1", model_index=1, item_category=ItemCategory.SUIT, resources=(ConditionalResources(None, None, ()), ), convert_resources=(ResourceConversion( resource_a, resource_b), )) current_resources = {resource_a: 5} # Run add_resource_gain_to_current_resources( pickup.resource_gain(current_resources), current_resources) # Assert assert current_resources == {resource_a: 0, resource_b: 5}
def test_create_missile_launcher(ammo_quantity: int, echoes_item_database, echoes_resource_database): # Setup missile = echoes_resource_database.get_by_type_and_index( ResourceType.ITEM, 44) missile_launcher = echoes_resource_database.get_by_type_and_index( ResourceType.ITEM, 73) temporary = echoes_resource_database.get_by_type_and_index( ResourceType.ITEM, 71) state = MajorItemState( include_copy_in_original_location=False, num_shuffled_pickups=0, num_included_in_starting_items=0, included_ammo=(ammo_quantity, ), ) # Run result = randovania.generator.item_pool.pickup_creator.create_major_item( echoes_item_database.major_items["Missile Launcher"], state, True, echoes_resource_database, echoes_item_database.ammo["Missile Expansion"], True) # Assert assert result == PickupEntry( name="Missile Launcher", resources=(ConditionalResources( "Missile Launcher", None, resources=( (missile_launcher, 1), (missile, ammo_quantity), (echoes_resource_database.item_percentage, 1), )), ), convert_resources=(ResourceConversion(source=temporary, target=missile), ), model_index=24, item_category=ItemCategory.MISSILE, )
def test_assign_pickup_to_starting_items(empty_patches, database): # Setup starting = state.State({}, (), 99, None, empty_patches, None, database) resource_a = SimpleResourceInfo(1, "A", "A", ResourceType.ITEM) resource_b = SimpleResourceInfo(2, "B", "B", ResourceType.ITEM) p = PickupEntry("A", 2, ItemCategory.SUIT, resources=(ConditionalResources(None, None, ( (resource_a, 5), (database.item_percentage, 1), )), ), convert_resources=(ResourceConversion( resource_b, resource_a), )) # Run final = starting.assign_pickup_to_starting_items(p) # Assert assert final.patches.starting_items == {resource_a: 5, resource_b: 0} assert final.resources == {resource_a: 5, resource_b: 0}
def test_create_pickup_for(percentage: bool, has_convert: bool, echoes_resource_database): # Setup item_a = echoes_resource_database.get_by_type_and_index( ResourceType.ITEM, 10) item_b = echoes_resource_database.get_by_type_and_index( ResourceType.ITEM, 15) item_c = echoes_resource_database.get_by_type_and_index( ResourceType.ITEM, 18) ammo_a = echoes_resource_database.get_by_type_and_index( ResourceType.ITEM, 40) ammo_b = echoes_resource_database.get_by_type_and_index( ResourceType.ITEM, 42) temporary_a = echoes_resource_database.get_by_type_and_index( ResourceType.ITEM, 71) temporary_b = echoes_resource_database.get_by_type_and_index( ResourceType.ITEM, 72) major_item = MajorItem( name="The Item", item_category=ItemCategory.MORPH_BALL, model_index=1337, progression=(10, 15, 18), ammo_index=(40, 42), converts_indices=(71, 72) if has_convert else (), required=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), ) def _create_resources(item): if percentage: return ( (item, 1), (ammo_a, 10), (ammo_b, 20), (echoes_resource_database.item_percentage, 1), ) else: return ( (item, 1), (ammo_a, 10), (ammo_b, 20), ) # Run result = randovania.generator.item_pool.pickup_creator.create_major_item( major_item, state, percentage, echoes_resource_database, None, False) # Assert assert result == PickupEntry( name="The Item", model_index=1337, resources=( ConditionalResources( name="Dark Visor", item=None, resources=_create_resources(item_a), ), ConditionalResources( name="Morph Ball", item=item_a, resources=_create_resources(item_b), ), ConditionalResources( name="Morph Ball Bomb", item=item_b, resources=_create_resources(item_c), ), ), convert_resources=( ResourceConversion(source=temporary_a, target=ammo_a), ResourceConversion(source=temporary_b, target=ammo_b), ) if has_convert else (), item_category=ItemCategory.MORPH_BALL, probability_offset=5, )
def test_create_pickup_list(model_style: PickupModelStyle, empty_patches): # Setup has_scan_text = model_style in {PickupModelStyle.ALL_VISIBLE, PickupModelStyle.HIDE_MODEL} rng = Random(5000) useless_resource = SimpleResourceInfo(0, "Useless", "Useless", ResourceType.ITEM) resource_a = SimpleResourceInfo(1, "A", "A", ResourceType.ITEM) resource_b = SimpleResourceInfo(2, "B", "B", ResourceType.ITEM) pickup_a = PickupEntry("A", 1, ItemCategory.TEMPLE_KEY, ( ConditionalResources(None, None, ((resource_a, 1),)), )) pickup_b = PickupEntry("B", 2, ItemCategory.SUIT, ( ConditionalResources(None, None, ((resource_b, 1), (resource_a, 1))), ConditionalResources(None, resource_b, ((resource_a, 5),)) )) pickup_c = PickupEntry("C", 2, ItemCategory.EXPANSION, resources=( ConditionalResources(None, None, ((resource_b, 2), (resource_a, 1))), ), convert_resources=( ResourceConversion(useless_resource, resource_a), )) useless_pickup = PickupEntry("Useless", 0, ItemCategory.ETM, ( ConditionalResources(None, None, ((useless_resource, 1),)), )) patches = empty_patches.assign_pickup_assignment({ PickupIndex(0): pickup_a, PickupIndex(2): pickup_b, PickupIndex(3): pickup_a, PickupIndex(4): pickup_c, }) # Run result = patcher_file._create_pickup_list(patches, useless_pickup, 5, rng, model_style, PickupModelDataSource.ETM, None, ) # Assert assert len(result) == 5 assert result[0] == { "pickup_index": 0, "model_index": 1 if model_style == PickupModelStyle.ALL_VISIBLE else 30, "scan": "A" if has_scan_text else "Unknown item", "hud_text": ["A acquired!"] if model_style != PickupModelStyle.HIDE_ALL else ['Unknown item acquired!'], "sound_index": 1 if model_style == PickupModelStyle.ALL_VISIBLE else 0, "jingle_index": 2 if model_style == PickupModelStyle.ALL_VISIBLE else 0, "resources": [ { "index": 1, "amount": 1 } ], "conditional_resources": [], "convert": [], } assert result[1] == { "pickup_index": 1, "scan": "Useless" if has_scan_text else "Unknown item", "model_index": 0 if model_style == PickupModelStyle.ALL_VISIBLE else 30, "hud_text": ["Useless acquired!"] if model_style != PickupModelStyle.HIDE_ALL else ['Unknown item acquired!'], "sound_index": 0, "jingle_index": 0, "resources": [ { "index": 0, "amount": 1 } ], "conditional_resources": [], "convert": [], } assert result[2] == { "pickup_index": 2, "scan": "B" if has_scan_text else "Unknown item", "model_index": 2 if model_style == PickupModelStyle.ALL_VISIBLE else 30, "hud_text": ["B acquired!", "B acquired!"] if model_style != PickupModelStyle.HIDE_ALL else [ 'Unknown item acquired!', 'Unknown item acquired!'], "sound_index": 0, "jingle_index": 1 if model_style == PickupModelStyle.ALL_VISIBLE else 0, "resources": [ { "index": 2, "amount": 1 }, { "index": 1, "amount": 1 } ], "conditional_resources": [{ "item": 2, "resources": [ { "index": 1, "amount": 5 } ] }], "convert": [], } assert result[3] == { "pickup_index": 3, "scan": "A" if has_scan_text else "Unknown item", "model_index": 1 if model_style == PickupModelStyle.ALL_VISIBLE else 30, "hud_text": ["A acquired!"] if model_style != PickupModelStyle.HIDE_ALL else ['Unknown item acquired!'], "sound_index": 1 if model_style == PickupModelStyle.ALL_VISIBLE else 0, "jingle_index": 2 if model_style == PickupModelStyle.ALL_VISIBLE else 0, "resources": [ { "index": 1, "amount": 1 } ], "conditional_resources": [], "convert": [], } assert result[4] == { "pickup_index": 4, "scan": "C that provides 2 B and 1 A" if has_scan_text else "Unknown item", "model_index": 2 if model_style == PickupModelStyle.ALL_VISIBLE else 30, "hud_text": ["C acquired!"] if model_style != PickupModelStyle.HIDE_ALL else ['Unknown item acquired!'], "sound_index": 0, "jingle_index": 0, "resources": [ { "index": 2, "amount": 2 }, { "index": 1, "amount": 1 } ], "conditional_resources": [], "convert": [ { "from_item": 0, "to_item": 1, "clear_source": True, "overwrite_target": False, } ], }
def test_create_pickup_list(model_style: PickupModelStyle, empty_patches): # Setup has_scan_text = model_style in { PickupModelStyle.ALL_VISIBLE, PickupModelStyle.HIDE_MODEL } rng = Random(5000) model_0 = MagicMock(spec=PickupModel) model_1 = MagicMock(spec=PickupModel) model_2 = MagicMock(spec=PickupModel) useless_model = PickupModel( game=RandovaniaGame.PRIME2, name="EnergyTransferModule", ) useless_resource = ItemResourceInfo(0, "Useless", "Useless", 10, None) resource_a = ItemResourceInfo(1, "A", "A", 10, None) resource_b = ItemResourceInfo(2, "B", "B", 10, None) pickup_a = PickupEntry( "P-A", model_1, ItemCategory.TEMPLE_KEY, ItemCategory.KEY, progression=((resource_a, 1), ), ) pickup_b = PickupEntry( "P-B", model_2, ItemCategory.SUIT, ItemCategory.LIFE_SUPPORT, progression=((resource_b, 1), (resource_a, 5)), ) pickup_c = PickupEntry("P-C", model_2, ItemCategory.EXPANSION, ItemCategory.MISSILE_RELATED, progression=tuple(), extra_resources=((resource_b, 2), (resource_a, 1)), unlocks_resource=True, resource_lock=ResourceLock(resource_a, resource_a, useless_resource)) useless_pickup = PickupEntry("P-Useless", model_0, ItemCategory.ETM, ItemCategory.ETM, progression=((useless_resource, 1), )) patches = empty_patches.assign_pickup_assignment({ PickupIndex(0): PickupTarget(pickup_a, 0), PickupIndex(2): PickupTarget(pickup_b, 0), PickupIndex(3): PickupTarget(pickup_a, 0), PickupIndex(4): PickupTarget(pickup_c, 0), }) creator = pickup_exporter.PickupExporterSolo( pickup_exporter.GenericAcquiredMemo()) # Run result = pickup_exporter.export_all_indices( patches, PickupTarget(useless_pickup, 0), 5, rng, model_style, PickupModelDataSource.ETM, creator, pickup_creator.create_visual_etm(), ) # Assert assert len(result) == 5 assert result[0] == pickup_exporter.ExportedPickupDetails( index=PickupIndex(0), scan_text="P-A" if has_scan_text else "Unknown item", hud_text=["A acquired!"] if model_style != PickupModelStyle.HIDE_ALL else ['Unknown item acquired!'], conditional_resources=[ ConditionalResources("A", None, ((resource_a, 1), )) ], conversion=[], model=model_1 if model_style == PickupModelStyle.ALL_VISIBLE else useless_model, ) assert result[1] == pickup_exporter.ExportedPickupDetails( index=PickupIndex(1), scan_text="P-Useless" if has_scan_text else "Unknown item", hud_text=["Useless acquired!"] if model_style != PickupModelStyle.HIDE_ALL else ['Unknown item acquired!'], conditional_resources=[ ConditionalResources("Useless", None, ((useless_resource, 1), )) ], conversion=[], model=model_0 if model_style == PickupModelStyle.ALL_VISIBLE else useless_model, ) assert result[2] == pickup_exporter.ExportedPickupDetails( index=PickupIndex(2), scan_text="P-B. Provides the following in order: B, A" if has_scan_text else "Unknown item", hud_text=["B acquired!", "A acquired!"] if model_style != PickupModelStyle.HIDE_ALL else ['Unknown item acquired!', 'Unknown item acquired!'], conditional_resources=[ ConditionalResources("B", None, ((resource_b, 1), )), ConditionalResources("A", resource_b, ((resource_a, 5), )), ], conversion=[], model=model_2 if model_style == PickupModelStyle.ALL_VISIBLE else useless_model, ) assert result[3] == pickup_exporter.ExportedPickupDetails( index=PickupIndex(3), scan_text="P-A" if has_scan_text else "Unknown item", hud_text=["A acquired!"] if model_style != PickupModelStyle.HIDE_ALL else ['Unknown item acquired!'], conditional_resources=[ ConditionalResources("A", None, ((resource_a, 1), )) ], conversion=[], model=model_1 if model_style == PickupModelStyle.ALL_VISIBLE else useless_model, ) assert result[4] == pickup_exporter.ExportedPickupDetails( index=PickupIndex(4), scan_text="P-C. Provides 2 B and 1 A" if has_scan_text else "Unknown item", hud_text=["P-C acquired!"] if model_style != PickupModelStyle.HIDE_ALL else ['Unknown item acquired!'], conditional_resources=[ ConditionalResources("P-C", None, ( (resource_b, 2), (resource_a, 1), )) ], conversion=[ ResourceConversion(source=useless_resource, target=resource_a) ], model=model_2 if model_style == PickupModelStyle.ALL_VISIBLE else useless_model, )
def decode_resource_conversion(self, decoder: BitPackDecoder) -> ResourceConversion: return ResourceConversion( source=self._decode_item(decoder), target=self._decode_item(decoder), )
def create_major_item( item: MajorItem, state: MajorItemState, include_percentage: bool, resource_database: ResourceDatabase, ammo: Optional[Ammo], ammo_requires_major_item: bool, ) -> PickupEntry: """ Creates a Pickup for the given MajorItem :param include_percentage: :param state: :param item: :param resource_database: :param ammo: :param ammo_requires_major_item: :return: """ def _create_resources( base_resource: Optional[int], temporary_ammo: bool = False) -> Tuple[ResourceQuantity, ...]: resources = [] if base_resource is not None: resources.append((resource_database.get_item(base_resource), 1)) for ammo_index, ammo_count in zip( ammo.temporaries if temporary_ammo else item.ammo_index, state.included_ammo): resources.append( (resource_database.get_item(ammo_index), ammo_count)) if include_percentage: resources.append((resource_database.item_percentage, 1)) return tuple(resources) if item.progression: if ammo_requires_major_item and ammo.unlocked_by != item.progression[ 0] and ammo.unlocked_by is not None: if len(item.progression) != 1: raise InvalidConfiguration(( "Item {item.name} uses ammo {ammo.name} that is locked behind {ammo.unlocked_by}," "but it also has progression. This is unsupported." ).format( ammo=ammo, item=item, )) name = resource_database.get_item(item.progression[0]).long_name conditional_resources = (ConditionalResources( name=name, item=None, resources=_create_resources(item.progression[0], True)), ConditionalResources( name=name, item=resource_database.get_item( ammo.unlocked_by), resources=_create_resources( item.progression[0]))) else: conditional_resources = tuple( ConditionalResources( name=resource_database.get_item( item.progression[i]).long_name, item=resource_database.get_item(item.progression[i - 1] ) if i > 0 else None, resources=_create_resources(progression)) for i, progression in enumerate(item.progression)) else: conditional_resources = (ConditionalResources( name=item.name, item=None, resources=_create_resources(None)), ) if item.converts_indices: assert len(item.converts_indices) == len(item.ammo_index) convert_resources = tuple( ResourceConversion( source=resource_database.get_item(source), target=resource_database.get_item(target), ) for source, target in zip(item.converts_indices, item.ammo_index)) else: convert_resources = tuple() return PickupEntry( name=item.name, resources=conditional_resources, model_index=item.model_index, item_category=item.item_category, broad_category=item.broad_category, probability_offset=item.probability_offset, probability_multiplier=item.probability_multiplier, convert_resources=convert_resources, )
def test_create_pickup_list(model_style: PickupModelStyle, empty_patches, generic_item_category, blank_resource_db): # Setup has_scan_text = model_style in {PickupModelStyle.ALL_VISIBLE, PickupModelStyle.HIDE_MODEL} rng = Random(5000) model_0 = MagicMock(spec=PickupModel) model_1 = MagicMock(spec=PickupModel) model_2 = MagicMock(spec=PickupModel) useless_model = PickupModel( game=RandovaniaGame.METROID_PRIME_ECHOES, name="EnergyTransferModule", ) useless_resource = ItemResourceInfo(0, "Useless", "Useless", 10) resource_a = ItemResourceInfo(1, "A", "A", 10) resource_b = ItemResourceInfo(2, "B", "B", 10) pickup_a = PickupEntry("P-A", model_1, generic_item_category, generic_item_category, progression=((resource_a, 1),), ) pickup_b = PickupEntry("P-B", model_2, generic_item_category, generic_item_category, progression=((resource_b, 1), (resource_a, 5)), ) pickup_c = PickupEntry("P-C", model_2, AMMO_ITEM_CATEGORY, generic_item_category, progression=tuple(), extra_resources=((resource_b, 2), (resource_a, 1)), unlocks_resource=True, resource_lock=ResourceLock(resource_a, resource_a, useless_resource)) useless_pickup = PickupEntry("P-Useless", model_0, USELESS_ITEM_CATEGORY, USELESS_ITEM_CATEGORY, progression=((useless_resource, 1),)) patches = empty_patches.assign_new_pickups([ (PickupIndex(0), PickupTarget(pickup_a, 0)), (PickupIndex(2), PickupTarget(pickup_b, 0)), (PickupIndex(3), PickupTarget(pickup_a, 0)), (PickupIndex(4), PickupTarget(pickup_c, 0)), ]) creator = pickup_exporter.PickupExporterSolo(pickup_exporter.GenericAcquiredMemo()) world_list = MagicMock() world_list.iterate_nodes.return_value = [ PickupNode(NodeIdentifier.create("World", "Area", f"Name {i}"), i, False, None, "", ("default",), {}, PickupIndex(i), False) for i in range(5) ] # Run result = pickup_exporter.export_all_indices( patches, PickupTarget(useless_pickup, 0), world_list, rng, model_style, PickupModelDataSource.ETM, creator, pickup_creator.create_visual_etm(), ) # Assert assert len(result) == 5 assert result[0] == pickup_exporter.ExportedPickupDetails( index=PickupIndex(0), scan_text="P-A" if has_scan_text else "Unknown item", hud_text=["A acquired!"] if model_style != PickupModelStyle.HIDE_ALL else ['Unknown item acquired!'], conditional_resources=[ConditionalResources("A", None, ((resource_a, 1),))], conversion=[], model=model_1 if model_style == PickupModelStyle.ALL_VISIBLE else useless_model, other_player=False, original_pickup=pickup_a, ) assert result[1] == pickup_exporter.ExportedPickupDetails( index=PickupIndex(1), scan_text="P-Useless" if has_scan_text else "Unknown item", hud_text=["Useless acquired!"] if model_style != PickupModelStyle.HIDE_ALL else ['Unknown item acquired!'], conditional_resources=[ConditionalResources("Useless", None, ((useless_resource, 1),))], conversion=[], model=model_0 if model_style == PickupModelStyle.ALL_VISIBLE else useless_model, other_player=False, original_pickup=useless_pickup, ) assert result[2] == pickup_exporter.ExportedPickupDetails( index=PickupIndex(2), scan_text="P-B. Provides the following in order: B, A" if has_scan_text else "Unknown item", hud_text=["B acquired!", "A acquired!"] if model_style != PickupModelStyle.HIDE_ALL else [ 'Unknown item acquired!', 'Unknown item acquired!'], conditional_resources=[ ConditionalResources("B", None, ((resource_b, 1),)), ConditionalResources("A", resource_b, ((resource_a, 5),)), ], conversion=[], model=model_2 if model_style == PickupModelStyle.ALL_VISIBLE else useless_model, other_player=False, original_pickup=pickup_b, ) assert result[3] == pickup_exporter.ExportedPickupDetails( index=PickupIndex(3), scan_text="P-A" if has_scan_text else "Unknown item", hud_text=["A acquired!"] if model_style != PickupModelStyle.HIDE_ALL else ['Unknown item acquired!'], conditional_resources=[ConditionalResources("A", None, ((resource_a, 1),))], conversion=[], model=model_1 if model_style == PickupModelStyle.ALL_VISIBLE else useless_model, other_player=False, original_pickup=pickup_a, ) assert result[4] == pickup_exporter.ExportedPickupDetails( index=PickupIndex(4), scan_text="P-C. Provides 2 B and 1 A" if has_scan_text else "Unknown item", hud_text=["P-C acquired!"] if model_style != PickupModelStyle.HIDE_ALL else ['Unknown item acquired!'], conditional_resources=[ConditionalResources("P-C", None, ( (resource_b, 2), (resource_a, 1), ))], conversion=[ResourceConversion(source=useless_resource, target=resource_a)], model=model_2 if model_style == PickupModelStyle.ALL_VISIBLE else useless_model, other_player=False, original_pickup=pickup_c, )