예제 #1
0
def test_state_change_to_starting(skip_qtbot, echoes_item_database,
                                  echoes_resource_database):
    item = [
        item for item in echoes_item_database.major_items.values()
        if not item.required
    ][0]
    state = MajorItemState(
        include_copy_in_original_location=False,
        num_shuffled_pickups=1,
        num_included_in_starting_items=0,
        included_ammo=(),
    )

    # Run
    popup = ItemConfigurationPopup(None, item, state, echoes_resource_database)
    skip_qtbot.addWidget(popup)
    skip_qtbot.mouseClick(popup.starting_radio, QtCore.Qt.LeftButton)

    # Assert
    assert popup.state == MajorItemState(
        include_copy_in_original_location=False,
        num_shuffled_pickups=0,
        num_included_in_starting_items=1,
        included_ammo=(),
    )
예제 #2
0
def test_state_required(skip_qtbot, echoes_item_database,
                        echoes_resource_database):
    item = [
        item for item in echoes_item_database.major_items.values()
        if item.required
    ][0]
    state = MajorItemState(
        include_copy_in_original_location=False,
        num_shuffled_pickups=1,
        num_included_in_starting_items=0,
        included_ammo=(),
    )

    # Run
    popup = ItemConfigurationWidget(None, item, state,
                                    echoes_resource_database)
    skip_qtbot.addWidget(popup)

    # Assert
    assert popup.state == MajorItemState(
        include_copy_in_original_location=False,
        num_shuffled_pickups=0,
        num_included_in_starting_items=1,
        included_ammo=(),
    )
    def from_json(cls, value: dict,
                  game: RandovaniaGame) -> "MajorItemsConfiguration":
        item_database = default_database.item_database_for_game(game)

        items_state = {}
        for name, item in item_database.major_items.items():
            if name in value["items_state"]:
                state = MajorItemState.from_json(value["items_state"][name])
            else:
                state = MajorItemState()
            items_state[item] = state

        default_items = {}
        for category, options in item_database.default_items.items():
            default_items[category] = item_database.major_items[
                value["default_items"][category.value]]
            if default_items[category] not in options:
                raise ValueError(
                    f"Category {category} has {default_items[category]} as default item, "
                    f"but that's not a valid option.")

        return cls(
            items_state=items_state,
            default_items=default_items,
            minimum_random_starting_items=value[
                "minimum_random_starting_items"],
            maximum_random_starting_items=value[
                "maximum_random_starting_items"],
        )
예제 #4
0
    def change_progressive(self, is_progressive: bool):
        with self._editor as editor:
            if is_progressive:
                non_progressive_state = MajorItemState()
                progressive_state = MajorItemState(num_shuffled_pickups=len(self.non_progressive_items))
            else:
                non_progressive_state = MajorItemState(num_shuffled_pickups=1)
                progressive_state = MajorItemState()

            new_states = {self.progressive_item: progressive_state}
            for item in self.non_progressive_items:
                new_states[item] = non_progressive_state

            editor.major_items_configuration = editor.major_items_configuration.replace_states(new_states)
예제 #5
0
 def state(self) -> MajorItemState:
     if self.included_box.isChecked():
         return MajorItemState(
             include_copy_in_original_location=self.vanilla_radio.isChecked(),
             num_shuffled_pickups=self.shuffled_spinbox.value() if self.shuffled_radio.isChecked() else 0,
             num_included_in_starting_items=1 if self.starting_radio.isChecked() else 0,
             included_ammo=self.included_ammo,
         )
     else:
         return MajorItemState(
             include_copy_in_original_location=False,
             num_shuffled_pickups=0,
             num_included_in_starting_items=0,
             included_ammo=self.included_ammo,
         )
예제 #6
0
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
    locked_conditional = (
        ConditionalResources("Seeker Launcher",
                             None,
                             resources=(
                                 (seeker_launcher, 1),
                                 (temporary, ammo_quantity),
                                 (echoes_resource_database.item_percentage, 1),
                             )),
        ConditionalResources("Seeker Launcher",
                             missile_launcher,
                             resources=(
                                 (seeker_launcher, 1),
                                 (missile, ammo_quantity),
                                 (echoes_resource_database.item_percentage, 1),
                             )),
    )
    normal_resources = (ConditionalResources(
        "Seeker Launcher",
        None,
        resources=(
            (seeker_launcher, 1),
            (missile, ammo_quantity),
            (echoes_resource_database.item_percentage, 1),
        )), )

    assert result == PickupEntry(
        name="Seeker Launcher",
        resources=locked_conditional
        if ammo_requires_major_item else normal_resources,
        model_index=25,
        item_category=ItemCategory.MISSILE,
        broad_category=ItemCategory.MISSILE_RELATED,
    )
예제 #7
0
    def bit_pack_unpack(cls, decoder: BitPackDecoder,
                        metadata) -> "MajorItemsConfiguration":
        reference: MajorItemsConfiguration = metadata["reference"]

        num_items = decoder.decode_single(len(reference.items_state))
        indices_with_custom = {
            decoder.decode_single(len(reference.items_state))
            for _ in range(num_items)
        }

        items_state = {}

        for index, item in enumerate(reference.items_state.keys()):
            if index in indices_with_custom:
                items_state[item] = MajorItemState.bit_pack_unpack(
                    decoder, item)
            else:
                items_state[item] = reference.items_state[item]

        minimum, maximum = decoder.decode(RANDOM_STARTING_ITEMS_LIMIT,
                                          RANDOM_STARTING_ITEMS_LIMIT)

        return cls(items_state,
                   minimum_random_starting_items=minimum,
                   maximum_random_starting_items=maximum)
예제 #8
0
 def _on_default_item_updated(self, category: ItemCategory, combo: QtWidgets.QComboBox, _):
     with self._editor as editor:
         new_config = editor.major_items_configuration
         new_config = new_config.replace_default_item(category, combo.currentData())
         new_config = new_config.replace_state_for_item(combo.currentData(),
                                                        MajorItemState(num_included_in_starting_items=1))
         editor.major_items_configuration = new_config
    def bit_pack_unpack(cls, decoder: BitPackDecoder,
                        metadata) -> "MajorItemsConfiguration":
        from randovania.game_description import default_database
        item_database = default_database.default_prime2_item_database()

        progressive_suit = bitpacking.decode_bool(decoder)
        progressive_grapple = bitpacking.decode_bool(decoder)
        progressive_launcher = bitpacking.decode_bool(decoder)

        default = MajorItemsConfiguration.default()
        num_items = decoder.decode_single(len(default.items_state))
        indices_with_custom = {
            decoder.decode_single(len(default.items_state))
            for _ in range(num_items)
        }

        items_state = {}

        for index, item in enumerate(item_database.major_items.values()):
            if index in indices_with_custom:
                items_state[item] = MajorItemState.bit_pack_unpack(
                    decoder, item)
            else:
                items_state[item] = default.items_state[item]

        minimum, maximum = decoder.decode(RANDOM_STARTING_ITEMS_LIMIT,
                                          RANDOM_STARTING_ITEMS_LIMIT)

        return cls(items_state,
                   progressive_suit=progressive_suit,
                   progressive_grapple=progressive_grapple,
                   progressive_launcher=progressive_launcher,
                   minimum_random_starting_items=minimum,
                   maximum_random_starting_items=maximum)
예제 #10
0
    def from_json(cls, value: dict,
                  item_database: ItemDatabase) -> "MajorItemsConfiguration":
        items_state = {}
        for name, item in item_database.major_items.items():
            if name in value["items_state"]:
                state = MajorItemState.from_json(value["items_state"][name])
            else:
                state = MajorItemState()
            items_state[item] = state

        return cls(
            items_state=items_state,
            minimum_random_starting_items=value[
                "minimum_random_starting_items"],
            maximum_random_starting_items=value[
                "maximum_random_starting_items"],
        )
예제 #11
0
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,
    )
예제 #12
0
def test_decode(state_with_data):
    # Setup
    item, data, expected = state_with_data

    # Run
    decoder = BitPackDecoder(data)
    result = MajorItemState.bit_pack_unpack(decoder, item)

    # Assert
    assert result == expected
예제 #13
0
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"
예제 #14
0
 def _create_state(self,
                   *,
                   include_copy_in_original_location=False,
                   num_shuffled_pickups=0,
                   num_included_in_starting_items=0,
                   ):
     return MajorItemState(
         include_copy_in_original_location=include_copy_in_original_location,
         num_shuffled_pickups=num_shuffled_pickups,
         num_included_in_starting_items=num_included_in_starting_items,
         included_ammo=self.included_ammo,
     )
예제 #15
0
def _state_with_data(request):
    item = MajorItem(
        name="Item Name",
        item_category=ItemCategory(request.param.get("category", "visor")),
        model_index=0,
        progression=(),
        ammo_index=request.param.get("ammo_index", ()),
        required=True,
        original_index=None,
        probability_offset=0,
    )
    return item, request.param["encoded"], MajorItemState.from_json(request.param["json"])
예제 #16
0
def get_default_major_items_configurations() -> MajorItemsConfiguration:
    item_database = default_prime2_item_database()

    with get_data_path().joinpath("item_database", "default_state", "major-items.json").open() as open_file:
        data = json.load(open_file)

    return MajorItemsConfiguration(
        items_state={
            item_database.major_items[name]: MajorItemState.from_json(state_data)
            for name, state_data in data["items_state"].items()
        }
    )
예제 #17
0
    def _change_progressive_launcher(self, has_progressive: bool):
        with self._options as options:
            major_configuration = options.major_items_configuration

            # Progressive launcher is added twice to the list so it's value is doubled.
            total = sum(major_configuration.items_state[item].included_ammo[0]
                        for item in [self._missile_launcher, self._seeker_launcher,
                                     self._progressive_launcher, self._progressive_launcher]) // 2

            if has_progressive:
                launcher_state = MajorItemState(included_ammo=(0,))
                seekers_state = MajorItemState(included_ammo=(0,))
                progressive_state = MajorItemState(num_shuffled_pickups=2, included_ammo=(total,))
            else:
                launcher_state = MajorItemState(num_shuffled_pickups=1, included_ammo=(total,))
                seekers_state = MajorItemState(num_shuffled_pickups=1, included_ammo=(total,))
                progressive_state = MajorItemState(included_ammo=(0,))

            major_configuration = major_configuration.replace_states({
                self._missile_launcher: launcher_state,
                self._seeker_launcher: seekers_state,
                self._progressive_launcher: progressive_state,
            })

            options.major_items_configuration = major_configuration
예제 #18
0
    def _create_major_item_boxes(self, item_database: ItemDatabase, resource_database: ResourceDatabase):
        for major_item in item_database.major_items.values():
            if major_item.required or major_item.item_category == ItemCategory.ENERGY_TANK:
                continue

            category_box, category_layout, elements = self._boxes_for_category[major_item.item_category]
            widget = ItemConfigurationWidget(None, major_item, MajorItemState(), resource_database)
            widget.Changed.connect(partial(self._on_major_item_updated, widget))

            row = category_layout.rowCount()
            category_layout.addWidget(widget, row, 0, 1, -1)

            elements[major_item] = widget
예제 #19
0
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)
예제 #20
0
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 from_json(cls, value: dict,
                  item_database: ItemDatabase) -> "MajorItemsConfiguration":
        default = cls.default()

        items_state = copy.copy(default.items_state)
        for name, state_data in value["items_state"].items():
            items_state[item_database.major_items[
                name]] = MajorItemState.from_json(state_data)

        return cls(
            items_state=items_state,
            progressive_suit=value["progressive_suit"],
            progressive_grapple=value["progressive_grapple"],
            minimum_random_starting_items=value[
                "minimum_random_starting_items"],
            maximum_random_starting_items=value[
                "maximum_random_starting_items"],
        )
예제 #22
0
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)
예제 #23
0
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,
    )
예제 #24
0
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 bit_pack_unpack(cls, decoder: BitPackDecoder,
                        metadata) -> "MajorItemsConfiguration":
        reference: MajorItemsConfiguration = metadata["reference"]

        num_items = decoder.decode_single(len(reference.items_state))
        indices_with_custom = {
            decoder.decode_single(len(reference.items_state))
            for _ in range(num_items)
        }

        items_state = {}

        for index, item in enumerate(reference.items_state.keys()):
            if index in indices_with_custom:
                items_state[item] = MajorItemState.bit_pack_unpack(
                    decoder, item)
            else:
                items_state[item] = reference.items_state[item]

        # default_items
        default_items = {}
        for category in reference.default_items.keys():
            all_major = [
                major for major in reference.items_state.keys()
                if major.item_category == category
            ]
            default_items[category] = decoder.decode_element(all_major)

        # random starting items
        minimum, maximum = decoder.decode(RANDOM_STARTING_ITEMS_LIMIT,
                                          RANDOM_STARTING_ITEMS_LIMIT)

        return cls(items_state,
                   default_items=default_items,
                   minimum_random_starting_items=minimum,
                   maximum_random_starting_items=maximum)
예제 #26
0
    def _change_progressive_grapple(self, has_progressive: bool):
        with self._editor as options:
            major_configuration = options.major_items_configuration

            if has_progressive:
                grapple_state = MajorItemState()
                screw_state = MajorItemState()
                progressive_state = MajorItemState(num_shuffled_pickups=2)
            else:
                grapple_state = MajorItemState(num_shuffled_pickups=1)
                screw_state = MajorItemState(num_shuffled_pickups=1)
                progressive_state = MajorItemState()

            major_configuration = major_configuration.replace_states({
                self._grapple_beam: grapple_state,
                self._screw_attack: screw_state,
                self._progressive_grapple: progressive_state,
            })

            options.major_items_configuration = major_configuration
예제 #27
0
    def _change_progressive_suit(self, has_progressive: bool):
        with self._editor as options:
            major_configuration = options.major_items_configuration

            if has_progressive:
                dark_suit_state = MajorItemState()
                light_suit_state = MajorItemState()
                progressive_suit_state = MajorItemState(num_shuffled_pickups=2)
            else:
                dark_suit_state = MajorItemState(num_shuffled_pickups=1)
                light_suit_state = MajorItemState(num_shuffled_pickups=1)
                progressive_suit_state = MajorItemState()

            major_configuration = major_configuration.replace_states({
                self._dark_suit: dark_suit_state,
                self._light_suit: light_suit_state,
                self._progressive_suit: progressive_suit_state,
            })

            options.major_items_configuration = major_configuration
예제 #28
0
def test_blank_as_json():
    assert MajorItemState().as_json == {}
예제 #29
0
def test_blank_from_json():
    assert MajorItemState.from_json({}) == MajorItemState()
예제 #30
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,
    )