def test_decode_pickup(client, echoes_resource_database): data = ( b'\x88\xa8\xd0\xca@\x9c\xc2\xda\xca\xcc\x08\x8a\xdc\xca\xe4\xce' b'\xf2\xa8\xe4\xc2\xdc\xe6\xcc\xca\xe4\x9a\xde\xc8\xea\xd8\xcaB\x00p') expected_pickup = PickupEntry( name="The Name", model=PickupModel( game=RandovaniaGame.PRIME2, name="EnergyTransferModule", ), item_category=ItemCategory.MOVEMENT, broad_category=ItemCategory.MOVEMENT, progression=tuple(), ) # from randovania.bitpacking import bitpacking # from randovania.network_common.pickup_serializer import BitPackPickupEntry # new_data = bitpacking.pack_value(BitPackPickupEntry(expected_pickup, echoes_resource_database)) # assert new_data == data # Run pickup = multiworld_client._decode_pickup(data, echoes_resource_database) # Assert assert pickup == expected_pickup
def test_game_session_request_pickups_one_action( mock_session_description: PropertyMock, mock_get_resource_database: MagicMock, mock_get_pickup_target: MagicMock, flask_app, two_player_session, echoes_resource_database): # Setup sio = MagicMock() sio.get_current_user.return_value = database.User.get_by_id(1234) pickup = PickupEntry("A", PickupModel(echoes_resource_database.game_enum, "AmmoModel"), ItemCategory.TEMPLE_KEY, ItemCategory.KEY, progression=((echoes_resource_database.item[0], 1), )) mock_get_pickup_target.return_value = PickupTarget(pickup=pickup, player=0) mock_get_resource_database.return_value = echoes_resource_database # Run result = game_session.game_session_request_pickups(sio, 1) # Assert mock_get_resource_database.assert_called_once_with( mock_session_description.return_value, 0) mock_get_pickup_target.assert_called_once_with( mock_session_description.return_value, 1, 0) assert result == { "game": "prime2", "pickups": [{ 'provider_name': 'Other Name', 'pickup': 'C@fSK*4Fga_C{94xPb=' }] }
def test_decode_pickup(client: NetworkClient, echoes_resource_database, generic_item_category): data = ( "h^WxYK%Bzb%4P&bZe?<3c~o*?ZgXa3a!qe!b!=sZ&dS`%<kH75DmyE4E0aqZ0!yPSX#yJyqboamlgnXlAeaHwx" "y`|qjis5Tm5_m@(Ur7@&dS`%<kH75DmyE4E0aqZ0!yPSX#yJyqboamlgnXlAeaHwxy`|qjis5Tm5_m@(Ur6S-~" ) expected_pickup = PickupEntry( name="The Name", model=PickupModel( game=RandovaniaGame.METROID_PRIME_ECHOES, name="EnergyTransferModule", ), item_category=generic_item_category, broad_category=generic_item_category, progression=tuple(), ) # # Uncomment this to encode the data once again and get the new bytefield if it changed for some reason # from randovania.server.game_session import _base64_encode_pickup # new_data = _base64_encode_pickup(expected_pickup, echoes_resource_database) # assert new_data == data # Run pickup = _decode_pickup(data, echoes_resource_database) # Assert assert pickup == expected_pickup
def create_artifact(artifact_index: int, minimum_progression: int, resource_database: ResourceDatabase, ) -> PickupEntry: ARTIFACT_CATEGORY = ItemCategory( name="artifact", long_name="", hint_details=("an ", "artifact"), is_major=False, is_key=True ) return PickupEntry( name=prime_items.ARTIFACT_NAMES[artifact_index], progression=( (resource_database.get_item(prime_items.ARTIFACT_ITEMS[artifact_index]), 1), ), model=PickupModel( game=resource_database.game_enum, name=prime_items.ARTIFACT_MODEL[artifact_index], ), item_category=ARTIFACT_CATEGORY, broad_category=GENERIC_KEY_CATEGORY, probability_offset=0.25, required_progression=minimum_progression, )
def create_sky_temple_key( key_number: int, resource_database: ResourceDatabase, ) -> PickupEntry: """ :param key_number: :param resource_database: :return: """ SKY_TEMPLE_KEY_CATEGORY = ItemCategory(name="sky_temple_key", long_name="", hint_details=("a ", "Sky Temple Key"), is_major=False, is_key=True) return PickupEntry( name="Sky Temple Key {}".format(key_number + 1), progression=((resource_database.get_item( echoes_items.SKY_TEMPLE_KEY_ITEMS[key_number]), 1), ), model=PickupModel( game=resource_database.game_enum, name=echoes_items.SKY_TEMPLE_KEY_MODEL, ), item_category=SKY_TEMPLE_KEY_CATEGORY, broad_category=GENERIC_KEY_CATEGORY, probability_offset=3, )
def create_dark_temple_key( key_number: int, temple_index: int, resource_database: ResourceDatabase, ) -> PickupEntry: """ Creates a Dark Temple Key :param key_number: :param temple_index: The index of the temple: Dark Agon, Dark Torvus, Hive Temple :param resource_database: :return: """ TEMPLE_KEY_CATEGORY = ItemCategory(name="temple_key", long_name="", hint_details=("a ", "red Temple Key"), is_major=False, is_key=True) return PickupEntry( name=echoes_items.DARK_TEMPLE_KEY_NAMES[temple_index].format( key_number + 1), progression=((resource_database.get_item( echoes_items.DARK_TEMPLE_KEY_ITEMS[temple_index][key_number]), 1), ), model=PickupModel( game=resource_database.game_enum, name=echoes_items.DARK_TEMPLE_KEY_MODEL, ), item_category=TEMPLE_KEY_CATEGORY, broad_category=GENERIC_KEY_CATEGORY, probability_offset=3, )
def create_ammo_expansion( ammo: Ammo, ammo_count: Sequence[int], requires_major_item: bool, resource_database: ResourceDatabase, ) -> PickupEntry: """ Creates a Pickup for an expansion of the given ammo. :param ammo: :param ammo_count: :param requires_major_item: :param resource_database: :return: """ resources = [(resource_database.get_item(item), count) for item, count in zip(ammo.items, ammo_count)] if resource_database.item_percentage is not None: resources.append((resource_database.item_percentage, 1)) return PickupEntry( name=ammo.name, progression=(), extra_resources=tuple(resources), model=PickupModel( game=resource_database.game_enum, name=ammo.model_name, ), item_category=ammo.item_category, broad_category=ammo.broad_category, respects_lock=requires_major_item, resource_lock=ammo.create_resource_lock(resource_database), probability_multiplier=2, )
def create_energy_cell( cell_index: int, resource_database: ResourceDatabase, ) -> PickupEntry: ENERGY_CELL_CATEGORY = ItemCategory(name="energy_cell", long_name="", hint_details=("an ", "energy cell"), is_major=False, is_key=True) return PickupEntry( name=f"Energy Cell {cell_index + 1}", progression=((resource_database.get_item( corruption_items.ENERGY_CELL_ITEMS[cell_index]), 1), ), extra_resources=( (resource_database.get_item( corruption_items.ENERGY_CELL_TOTAL_ITEM), 1), (resource_database.item_percentage, 1), ), model=PickupModel( game=resource_database.game_enum, name=corruption_items.ENERGY_CELL_MODEL, ), item_category=ENERGY_CELL_CATEGORY, broad_category=GENERIC_KEY_CATEGORY, probability_offset=0.25, )
def test_run_validated_hud_text(): # Setup rng = MagicMock() rng.randint.return_value = 0 details = pickup_exporter.ExportedPickupDetails( index=PickupIndex(0), scan_text="scan", hud_text=["Energy Transfer Module acquired!"], conditional_resources=[ ConditionalResources(None, None, ()), ], conversion=[], model=PickupModel( game=RandovaniaGame.METROID_PRIME_ECHOES, name="EnergyTransferModule", ), other_player=False, original_pickup=None, ) # Run data = patch_data_factory.echoes_pickup_details_to_patcher(details, rng) # Assert assert data['hud_text'] == ['Run validated!']
def bit_pack_unpack(cls, decoder: BitPackDecoder, database: ResourceDatabase) -> PickupEntry: helper = DatabaseBitPackHelper(database) name = bitpacking.decode_string(decoder) model = PickupModel( game=RandovaniaGame.bit_pack_unpack(decoder, {}), name=bitpacking.decode_string(decoder), ) item_category = ItemCategory.bit_pack_unpack(decoder, {}) broad_category = ItemCategory.bit_pack_unpack(decoder, {}) progression = bitpacking.decode_tuple(decoder, helper.decode_resource_quantity) extra_resources = bitpacking.decode_tuple(decoder, helper.decode_resource_quantity) unlocks_resource = bitpacking.decode_bool(decoder) resource_lock = None if bitpacking.decode_bool(decoder): resource_lock = helper.decode_resource_lock(decoder) respects_lock = bitpacking.decode_bool(decoder) probability_offset = BitPackFloat.bit_pack_unpack(decoder, _PROBABILITY_OFFSET_META) probability_multiplier = BitPackFloat.bit_pack_unpack(decoder, _PROBABILITY_MULTIPLIER_META) return PickupEntry( name=name, model=model, item_category=item_category, broad_category=broad_category, progression=progression, extra_resources=extra_resources, unlocks_resource=unlocks_resource, resource_lock=resource_lock, respects_lock=respects_lock, probability_offset=probability_offset, probability_multiplier=probability_multiplier, )
def create_pickup_hint( pickup_assignment: PickupAssignment, world_list: WorldList, precision: HintItemPrecision, target: Optional[PickupTarget], players_config: PlayersConfiguration, include_owner: bool, ) -> PickupHint: """ :param pickup_assignment: :param world_list: :param precision: :param target: :param players_config: :param include_owner: :return: """ if target is None: target = PickupTarget( pickup=PickupEntry( name="Energy Transfer Module", progression=tuple(), model=PickupModel( game=RandovaniaGame.METROID_PRIME_ECHOES, name="EnergyTransferModule", ), item_category=USELESS_ITEM_CATEGORY, broad_category=USELESS_ITEM_CATEGORY, ), player=players_config.player_index, ) if precision is HintItemPrecision.GENERAL_CATEGORY: details = target.pickup.item_category.general_details elif precision is HintItemPrecision.PRECISE_CATEGORY: details = target.pickup.item_category.hint_details elif precision is HintItemPrecision.BROAD_CATEGORY: details = target.pickup.broad_category.hint_details elif precision is HintItemPrecision.DETAILED: details = _calculate_determiner(pickup_assignment, target.pickup, world_list), target.pickup.name elif precision is HintItemPrecision.NOTHING: details = "an ", "item" else: raise ValueError(f"Unknown precision: {precision}") determiner = Determiner(details[0]) player = None if include_owner and players_config.is_multiworld: player = players_config.player_names[target.player] return PickupHint(determiner, player, details[1])
def test_create_pickup_for(percentage: bool, echoes_resource_database): # Setup item_a = echoes_resource_database.get_item(10) item_b = echoes_resource_database.get_item(15) item_c = echoes_resource_database.get_item(18) ammo_a = echoes_resource_database.get_item(40) ammo_b = echoes_resource_database.get_item(42) major_item = MajorItem( name="The Item", item_category=ItemCategory.MORPH_BALL, broad_category=ItemCategory.MORPH_BALL_RELATED, model_name="SuperModel", progression=(10, 15, 18), ammo_index=(40, 42), 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), ) 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 = 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=PickupModel(echoes_resource_database.game_enum, "SuperModel"), progression=( (item_a, 1), (item_b, 1), (item_c, 1), ), extra_resources=extra_resources, item_category=ItemCategory.MORPH_BALL, broad_category=ItemCategory.MORPH_BALL_RELATED, probability_offset=5, respects_lock=False, )
def _make_pickup(item_category: ItemCategory): return PickupEntry( name="Pickup", model=PickupModel( game=RandovaniaGame.PRIME2, name="EnergyTransferModule", ), item_category=item_category, broad_category=item_category, progression=tuple(), )
def test_extra_resources_maximum(generic_item_category): item = ItemResourceInfo("Item", "Item", 2, None) msg = "Attempt to give 5 of Item, more than max capacity" with pytest.raises(ValueError, match=msg): PickupEntry(name="broken", model=PickupModel(RandovaniaGame.METROID_PRIME_ECHOES, "Nothing"), item_category=generic_item_category, broad_category=generic_item_category, progression=(), extra_resources=((item, 5), ))
def blank_pickup() -> PickupEntry: return PickupEntry( name="Blank Pickup", model=PickupModel( game=RandovaniaGame.PRIME2, name="EnergyTransferModule", ), item_category=ItemCategory.SUIT, broad_category=ItemCategory.LIFE_SUPPORT, progression=(), resource_lock=None, unlocks_resource=False, )
def blank_pickup(echoes_item_database) -> PickupEntry: return PickupEntry( name="Blank Pickup", model=PickupModel( game=RandovaniaGame.METROID_PRIME_ECHOES, name="EnergyTransferModule", ), item_category=echoes_item_database.item_categories["suit"], broad_category=echoes_item_database.item_categories["life_support"], progression=(), resource_lock=None, unlocks_resource=False, )
def test_prime1_pickup_details_to_patcher_shiny_missile( prime1_resource_database, other_player: bool): # Setup rng = MagicMock() rng.randint.return_value = 0 detail = pickup_exporter.ExportedPickupDetails( index=PickupIndex(15), scan_text="Your Missile Expansion. Provides 5 Missiles", hud_text=["Missile Expansion acquired!"], conditional_resources=[ ConditionalResources( None, None, ((prime1_resource_database.get_item_by_name("Missile"), 6), ), ) ], conversion=[], model=PickupModel(RandovaniaGame.METROID_PRIME, "Missile"), other_player=other_player, original_pickup=None, ) if other_player: shiny_stuff = { 'model': { 'game': 'prime1', 'name': 'Missile' }, 'scanText': 'Your Missile Expansion. Provides 5 Missiles', 'hudmemoText': 'Missile Expansion acquired!', } else: shiny_stuff = { 'model': { 'game': 'prime1', 'name': 'Shiny Missile' }, 'scanText': 'Your Shiny Missile Expansion. Provides 5 Missiles', 'hudmemoText': 'Shiny Missile Expansion acquired!', } # Run result = prime1_pickup_details_to_patcher(detail, False, rng) # Assert assert result == { 'type': 'Missile', 'currIncrease': 6, 'maxIncrease': 6, 'respawn': False, **shiny_stuff, }
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: """ extra_resources = [ (resource_database.get_item(ammo_index), ammo_count) for ammo_index, ammo_count in zip(item.ammo_index, state.included_ammo) ] if include_percentage and resource_database.item_percentage is not None: extra_resources.append((resource_database.item_percentage, 1)) def _create_resources(base_resource: Optional[str]) -> ResourceQuantity: # FIXME: hacky quantity for Hazard Shield quantity = 5 if item.name == "Hazard Shield" else 1 return resource_database.get_item(base_resource), quantity return PickupEntry( name=item.name, progression=tuple( _create_resources(progression) for progression in item.progression), extra_resources=tuple(extra_resources), model=PickupModel( game=resource_database.game_enum, name=item.model_name, ), item_category=item.item_category, broad_category=item.broad_category, probability_offset=item.probability_offset, probability_multiplier=item.probability_multiplier * state.priority, unlocks_resource=item.unlocks_ammo, respects_lock=ammo_requires_major_item, resource_lock=ammo.create_resource_lock(resource_database) if ammo is not None else None, )
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(44) missile_launcher = echoes_resource_database.get_item(73) seeker_launcher = echoes_resource_database.get_item(26) temporary = echoes_resource_database.get_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["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=ItemCategory.MISSILE, broad_category=ItemCategory.MISSILE_RELATED, respects_lock=ammo_requires_major_item, resource_lock=ResourceLock( locked_by=missile_launcher, temporary_item=temporary, item_to_lock=missile, ), )
def create_visual_etm() -> PickupEntry: """ Creates an ETM that should only be used as a visual pickup. :return: """ return PickupEntry( name="Unknown item", progression=tuple(), model=PickupModel( game=RandovaniaGame.METROID_PRIME_ECHOES, name=echoes_items.USELESS_PICKUP_MODEL, ), item_category=USELESS_ITEM_CATEGORY, broad_category=USELESS_ITEM_CATEGORY, )
def create_visual_etm() -> PickupEntry: """ Creates an ETM that should only be used as a visual pickup. :return: """ return PickupEntry( name="Unknown item", progression=tuple(), model=PickupModel( game=RandovaniaGame.PRIME2, name=echoes_items.USELESS_PICKUP_MODEL, ), item_category=ItemCategory.ETM, broad_category=ItemCategory.ETM, )
def create_nothing_pickup(resource_database: ResourceDatabase) -> PickupEntry: """ Creates a Nothing pickup. :param resource_database: :return: """ return PickupEntry( name="Nothing", progression=((resource_database.get_item_by_name("Nothing"), 1), ), model=PickupModel( game=resource_database.game_enum, name="Nothing", ), item_category=USELESS_ITEM_CATEGORY, broad_category=USELESS_ITEM_CATEGORY, )
def create_artifact(artifact_index: int, resource_database: ResourceDatabase, ) -> PickupEntry: return PickupEntry( name=prime_items.ARTIFACT_NAMES[artifact_index], progression=( (resource_database.get_item(prime_items.ARTIFACT_ITEMS[artifact_index]), 1), ), model=PickupModel( game=resource_database.game_enum, name=prime_items.ARTIFACT_MODEL[artifact_index], ), item_category=ItemCategory.TEMPLE_KEY, broad_category=ItemCategory.KEY, probability_offset=0.25, )
def create_echoes_useless_pickup( resource_database: ResourceDatabase) -> PickupEntry: """ Creates an Energy Transfer Module pickup. :param resource_database: :return: """ return PickupEntry( name="Energy Transfer Module", progression=((resource_database.get_item( echoes_items.USELESS_PICKUP_ITEM), 1), ), model=PickupModel( game=resource_database.game_enum, name=echoes_items.USELESS_PICKUP_MODEL, ), item_category=USELESS_ITEM_CATEGORY, broad_category=USELESS_ITEM_CATEGORY, )
def create_prime1_useless_pickup(resource_database: ResourceDatabase) -> PickupEntry: """ Creates a Nothing pickup. :param resource_database: :return: """ return PickupEntry( name="Nothing", progression=( (resource_database.get_item_by_name("Nothing"), 1), ), model=PickupModel( game=resource_database.game_enum, name="Nothing", ), item_category=ItemCategory.ETM, broad_category=ItemCategory.ETM, )
def adjust_model_names(patch_data: dict, assets_meta: dict, use_external_assets: bool): model_list = [] if use_external_assets: bad_models = {"prime2_MissileLauncher", "prime2_MissileExpansionPrime1"} model_list = list(set(assets_meta["items"]) - bad_models) for level in patch_data["levelData"].values(): for room in level["rooms"].values(): for pickup in room["pickups"]: model = PickupModel.from_json(pickup.pop("model")) if model.game == RandovaniaGame.METROID_PRIME: converted_model_name = model.name else: converted_model_name = "{}_{}".format(model.game.value, model.name) if converted_model_name not in model_list: converted_model_name = _MODEL_MAPPING.get((model.game, model.name), "Nothing") pickup['model'] = converted_model_name
def test_bit_pack_pickup_entry(has_convert: bool, echoes_resource_database, generic_item_category): # Setup name = "Some Random Name" if has_convert: resource_lock = ResourceLock( find_resource_info_with_long_name(echoes_resource_database.item, "Morph Ball"), find_resource_info_with_long_name(echoes_resource_database.item, "Item Percentage"), find_resource_info_with_long_name(echoes_resource_database.item, "Space Jump Boots"), ) else: resource_lock = None pickup = PickupEntry( name=name, model=PickupModel( game=RandovaniaGame.METROID_PRIME_CORRUPTION, name="HyperMissile", ), item_category=generic_item_category, broad_category=generic_item_category, progression=( (find_resource_info_with_long_name(echoes_resource_database.item, "Morph Ball"), 1), (find_resource_info_with_long_name(echoes_resource_database.item, "Grapple Beam"), 1), ), extra_resources=((find_resource_info_with_long_name( echoes_resource_database.item, "Item Percentage"), 5), ), resource_lock=resource_lock) # Run encoded = bitpacking.pack_value( BitPackPickupEntry(pickup, echoes_resource_database)) decoder = BitPackDecoder(encoded) decoded = BitPackPickupEntry.bit_pack_unpack(decoder, echoes_resource_database) # Assert assert pickup == decoded
def test_create_ammo_expansion(requires_major_item: bool, echoes_item_database, echoes_resource_database): # Setup primary_a = echoes_resource_database.get_item("MissileLauncher") ammo_a = echoes_resource_database.get_item("Missile") temporary_a = echoes_resource_database.get_item("Temporary1") ammo = Ammo( game=echoes_resource_database.game_enum, name="The Item", items=("Missile", ), broad_category=USELESS_ITEM_CATEGORY, unlocked_by="MissileLauncher", temporary="Temporary1", model_name="AmmoModel", ) ammo_count = (11, 150) # Run result = pickup_creator.create_ammo_expansion(ammo, ammo_count, requires_major_item, echoes_resource_database) # Assert assert result == PickupEntry( name="The Item", model=PickupModel(echoes_resource_database.game_enum, "AmmoModel"), progression=tuple(), extra_resources=( (ammo_a, ammo_count[0]), (echoes_resource_database.item_percentage, 1), ), item_category=AMMO_ITEM_CATEGORY, broad_category=USELESS_ITEM_CATEGORY, probability_offset=0, respects_lock=requires_major_item, resource_lock=ResourceLock( locked_by=primary_a, temporary_item=temporary_a, item_to_lock=ammo_a, ), )
def create_energy_cell(cell_index: int, resource_database: ResourceDatabase, ) -> PickupEntry: return PickupEntry( name=f"Energy Cell {cell_index + 1}", progression=( (resource_database.get_item(corruption_items.ENERGY_CELL_ITEMS[cell_index]), 1), ), extra_resources=( (resource_database.get_item(corruption_items.ENERGY_CELL_TOTAL_ITEM), 1), (resource_database.item_percentage, 1), ), model=PickupModel( game=resource_database.game_enum, name=corruption_items.ENERGY_CELL_MODEL, ), item_category=ItemCategory.TEMPLE_KEY, broad_category=ItemCategory.KEY, probability_offset=0.25, )
def create_sky_temple_key(key_number: int, resource_database: ResourceDatabase, ) -> PickupEntry: """ :param key_number: :param resource_database: :return: """ return PickupEntry( name="Sky Temple Key {}".format(key_number + 1), progression=((resource_database.get_item(echoes_items.SKY_TEMPLE_KEY_ITEMS[key_number]), 1),), model=PickupModel( game=resource_database.game_enum, name=echoes_items.SKY_TEMPLE_KEY_MODEL, ), item_category=ItemCategory.SKY_TEMPLE_KEY, broad_category=ItemCategory.KEY, probability_offset=3, )