Esempio n. 1
0
def test_decode(configuration_with_data):
    # Setup
    data, expected = configuration_with_data

    # Run
    decoder = BitPackDecoder(data)
    result = TrickLevelConfiguration.bit_pack_unpack(decoder, {
        "reference":
        TrickLevelConfiguration(False, {}, RandovaniaGame.PRIME2),
    })

    # Assert
    assert result == expected
def test_round_trip_generated_patches(echoes_game_data, default_preset):
    # Setup
    preset = dataclasses.replace(
        default_preset,
        base_preset_name=default_preset.name,
        configuration=dataclasses.replace(
            default_preset.configuration,
            trick_level=TrickLevelConfiguration(
                minimal_logic=True,
                specific_levels={},
                game=RandovaniaGame.PRIME2,
            )
        )
    )

    all_patches = generator._async_create_description(
        permalink=Permalink(
            seed_number=1000,
            spoiler=True,
            presets={0: preset},
        ),
        status_update=lambda x: None,
        attempts=0,
    ).all_patches

    # Run
    encoded = game_patches_serializer.serialize(all_patches, {0: echoes_game_data})
    decoded = game_patches_serializer.decode(encoded, {0: preset.configuration})

    # Assert
    assert all_patches == decoded
def test_create_permalink_logic(mock_print: MagicMock, ):
    # Setup
    args = MagicMock()
    args.trick_level = LayoutTrickLevel.HARD.value
    args.major_items_mode = False
    args.sky_temple_keys = LayoutSkyTempleKeyMode.ALL_BOSSES.value
    args.skip_item_loss = True
    args.seed = 15000
    args.menu_mod = False
    args.warp_to_start = False

    # Run
    randovania.cli.commands.create_permalink.create_permalink_logic(args)

    # Assert
    permalink = Permalink(
        seed_number=args.seed,
        spoiler=True,
        patcher_configuration=PatcherConfiguration(
            menu_mod=args.menu_mod,
            warp_to_start=args.warp_to_start,
        ),
        layout_configuration=LayoutConfiguration.from_params(
            trick_level_configuration=TrickLevelConfiguration(
                LayoutTrickLevel.HARD),
            sky_temple_keys=LayoutSkyTempleKeyMode.ALL_BOSSES,
            elevators=LayoutElevators.VANILLA,
            starting_location=StartingLocation.default(),
        ),
    )

    # Assert
    mock_print.assert_called_once_with(permalink)
Esempio n. 4
0
def test_decode(mock_dictionary_byte_hash: MagicMock):
    mock_dictionary_byte_hash.return_value = 120
    # We're mocking the database hash to avoid breaking tests every single time we change the database

    # This test should break whenever we change how permalinks are created
    # When this happens, we must bump the permalink version and change the tests
    encoded = "gAAAfReLCAAC4wAAAOaANg=="

    expected = Permalink(
        seed_number=1000,
        spoiler=True,
        patcher_configuration=PatcherConfiguration(
            menu_mod=True,
            warp_to_start=False,
        ),
        layout_configuration=LayoutConfiguration.from_params(
            trick_level_configuration=TrickLevelConfiguration(
                LayoutTrickLevel.HARD),
            elevators=LayoutElevators.RANDOMIZED,
        ),
    )

    # Uncomment this line to quickly get the new encoded permalink
    # assert expected.as_str == ""
    # print(expected.as_str)

    # Run
    link = Permalink.from_str(encoded)

    # Assert
    assert link == expected
Esempio n. 5
0
def test_reach_size_from_start(echoes_game_description,
                               default_layout_configuration, minimal_logic,
                               nodes, safe_nodes):
    # Setup
    specific_levels = {
        trick.short_name: LayoutTrickLevel.HYPERMODE
        for trick in echoes_game_description.resource_database.trick
    }

    layout_configuration = dataclasses.replace(
        default_layout_configuration,
        trick_level_configuration=TrickLevelConfiguration(
            minimal_logic=minimal_logic,
            specific_levels=specific_levels if not minimal_logic else {}),
    )
    player_pool = generator.create_player_pool(Random(15000),
                                               layout_configuration, 0)

    game, state = logic_bootstrap(layout_configuration, player_pool.game,
                                  player_pool.patches)

    # Run
    reach = GeneratorReach.reach_from_state(game, state)

    # Assert
    assert len(list(reach.nodes)) >= nodes
    assert len(list(reach.safe_nodes)) >= safe_nodes
Esempio n. 6
0
 def from_json_dict(cls, json_dict: dict) -> "LayoutConfiguration":
     return LayoutConfiguration(
         trick_level_configuration=TrickLevelConfiguration.from_json(
             json_dict["trick_level"]),
         damage_strictness=LayoutDamageStrictness(
             json_dict["damage_strictness"]),
         sky_temple_keys=LayoutSkyTempleKeyMode(
             json_dict["sky_temple_keys"]),
         elevators=LayoutElevators(json_dict["elevators"]),
         starting_location=StartingLocation.from_json(
             json_dict["starting_location"]),
         available_locations=AvailableLocationsConfiguration.from_json(
             json_dict["available_locations"]),
         major_items_configuration=MajorItemsConfiguration.from_json(
             json_dict["major_items_configuration"],
             default_prime2_item_database(),
         ),
         ammo_configuration=AmmoConfiguration.from_json(
             json_dict["ammo_configuration"],
             default_prime2_item_database(),
         ),
         translator_configuration=TranslatorConfiguration.from_json(
             json_dict["translator_configuration"]),
         hints=HintConfiguration.from_json(json_dict["hints"]),
         beam_configuration=BeamConfiguration.from_json(
             json_dict["beam_configuration"]),
         skip_final_bosses=json_dict["skip_final_bosses"],
         energy_per_tank=json_dict["energy_per_tank"],
         split_beam_ammo=json_dict["split_beam_ammo"],
     )
Esempio n. 7
0
def test_edit_layout_trick_level(editor: PresetEditor,
                                 initial_layout_configuration_params: dict,
                                 default_layout_configuration,
                                 new_trick_level: LayoutTrickLevel):
    # Setup
    editor._layout_configuration = dataclasses.replace(default_layout_configuration,
                                                       **initial_layout_configuration_params)
    editor._nested_autosave_level = 1

    # Run
    initial_layout_configuration_params["trick_level_configuration"] = TrickLevelConfiguration(new_trick_level)
    editor.set_layout_configuration_field("trick_level_configuration", TrickLevelConfiguration(new_trick_level))

    # Assert
    assert editor.layout_configuration == dataclasses.replace(default_layout_configuration,
                                                              **initial_layout_configuration_params)
def test_round_trip_generated_patches(echoes_game_data, preset_manager):
    # Setup
    preset = dataclasses.replace(
        preset_manager.default_preset,
        layout_configuration=dataclasses.replace(
            preset_manager.default_preset.layout_configuration,
            trick_level_configuration=TrickLevelConfiguration(
                global_level=LayoutTrickLevel.MINIMAL_RESTRICTIONS,
                specific_levels={},
            )))

    all_patches = generator._async_create_description(
        permalink=Permalink(
            seed_number=1000,
            spoiler=True,
            presets={0: preset},
        ),
        status_update=lambda x: None,
    ).all_patches

    # Run
    encoded = game_patches_serializer.serialize(all_patches,
                                                {0: echoes_game_data})
    decoded = game_patches_serializer.decode(encoded,
                                             {0: preset.layout_configuration})

    # Assert
    assert all_patches == decoded
Esempio n. 9
0
    def _setup_difficulties_menu(self):
        game = default_database.default_prime2_game_description()
        for i, trick_level in enumerate(LayoutTrickLevel):
            if trick_level not in {
                    LayoutTrickLevel.NO_TRICKS,
                    LayoutTrickLevel.MINIMAL_RESTRICTIONS
            }:
                difficulty_action = QAction(self)
                difficulty_action.setText(trick_level.long_name)
                self.menu_difficulties.addAction(difficulty_action)
                difficulty_action.triggered.connect(
                    functools.partial(self._open_difficulty_details_popup,
                                      trick_level))

        configurable_tricks = TrickLevelConfiguration.all_possible_tricks()
        tricks_in_use = used_tricks(game.world_list)

        for trick in sorted(game.resource_database.trick,
                            key=lambda _trick: _trick.long_name):
            if trick.index not in configurable_tricks or trick not in tricks_in_use:
                continue

            trick_menu = QMenu(self)
            trick_menu.setTitle(trick.long_name)
            self.menu_trick_details.addAction(trick_menu.menuAction())

            used_difficulties = difficulties_for_trick(game.world_list, trick)
            for i, trick_level in enumerate(LayoutTrickLevel):
                if trick_level in used_difficulties:
                    difficulty_action = QAction(self)
                    difficulty_action.setText(trick_level.long_name)
                    trick_menu.addAction(difficulty_action)
                    difficulty_action.triggered.connect(
                        functools.partial(self._open_trick_details_popup,
                                          trick, trick_level))
def test_round_trip_generated_patches(echoes_game_data):
    # Setup
    configuration = LayoutConfiguration.from_params(
        trick_level_configuration=TrickLevelConfiguration(
            global_level=LayoutTrickLevel.MINIMAL_RESTRICTIONS,
            specific_levels={},
        ))

    patches = generator._create_randomized_patches(
        permalink=Permalink(
            seed_number=1000,
            spoiler=True,
            patcher_configuration=PatcherConfiguration.default(),
            layout_configuration=configuration,
        ),
        game=data_reader.decode_data(echoes_game_data),
        status_update=lambda x: None,
    )

    # Run
    encoded = game_patches_serializer.serialize(patches, echoes_game_data)
    decoded = game_patches_serializer.decode(encoded, configuration)

    # Assert
    assert patches == decoded
Esempio n. 11
0
def test_round_trip_generated_patches(echoes_game_data, preset_manager):
    # Setup
    preset = dataclasses.replace(
        preset_manager.default_preset,
        layout_configuration=dataclasses.replace(
            preset_manager.default_preset.layout_configuration,
            trick_level_configuration=TrickLevelConfiguration(
                global_level=LayoutTrickLevel.MINIMAL_RESTRICTIONS,
                specific_levels={},
            )
        )
    )

    patches = generator._create_randomized_patches(
        permalink=Permalink(
            seed_number=1000,
            spoiler=True,
            preset=preset,
        ),
        game=data_reader.decode_data(echoes_game_data),
        status_update=lambda x: None,
    )

    # Run
    encoded = game_patches_serializer.serialize(patches, echoes_game_data)
    decoded = game_patches_serializer.decode(encoded, preset.layout_configuration)

    # Assert
    assert patches == decoded
Esempio n. 12
0
def test_edit_layout_trick_level(option: Options,
                                 initial_layout_configuration_params: dict,
                                 new_trick_level: LayoutTrickLevel):
    # Setup
    option._layout_configuration = LayoutConfiguration.from_params(
        **initial_layout_configuration_params)
    option._nested_autosave_level = 1

    # Run
    initial_layout_configuration_params[
        "trick_level_configuration"] = TrickLevelConfiguration(new_trick_level)
    option.set_layout_configuration_field(
        "trick_level_configuration", TrickLevelConfiguration(new_trick_level))

    # Assert
    assert option.layout_configuration == LayoutConfiguration.from_params(
        **initial_layout_configuration_params)
Esempio n. 13
0
def test_decode(mock_possible_tricks, configuration_with_data):
    # Setup
    data, expected = configuration_with_data

    # Run
    decoder = BitPackDecoder(data)
    result = TrickLevelConfiguration.bit_pack_unpack(decoder, {})

    # Assert
    assert result == expected
Esempio n. 14
0
def test_encode_no_tricks_are_removed():
    from_json = TrickLevelConfiguration.from_json({
        "minimal_logic": False,
        "specific_levels": {
            "Dash": "no-tricks"
        }
    })

    encoded = bitpacking._pack_encode_results([
        (value_argument, value_format)
        for value_argument, value_format in from_json.bit_pack_encode({})
    ])

    assert encoded == b'\x00\x00\x00\x00'

    decoder = BitPackDecoder(encoded)
    decoded = TrickLevelConfiguration.bit_pack_unpack(decoder, {})

    assert decoded.specific_levels == {}
Esempio n. 15
0
def test_set_level_for_trick_remove(echoes_resource_database):
    trick = echoes_resource_database.trick[0]
    config = TrickLevelConfiguration(False, {}, RandovaniaGame.PRIME2)

    assert config.level_for_trick(trick) == LayoutTrickLevel.DISABLED

    config = config.set_level_for_trick(trick, LayoutTrickLevel.ADVANCED)
    assert config.level_for_trick(trick) == LayoutTrickLevel.ADVANCED

    config = config.set_level_for_trick(trick, LayoutTrickLevel.DISABLED)
    assert config.level_for_trick(trick) == LayoutTrickLevel.DISABLED
Esempio n. 16
0
def get_layout_configuration_from_args(args) -> LayoutConfiguration:
    try:
        sky_temple_keys = int(args.sky_temple_keys)
    except ValueError:
        sky_temple_keys = args.sky_temple_keys

    # TODO: support for item loss
    return LayoutConfiguration.from_params(
        trick_level_configuration=TrickLevelConfiguration(
            LayoutTrickLevel(args.trick_level)),
        sky_temple_keys=LayoutSkyTempleKeyMode(sky_temple_keys),
        elevators=LayoutElevators.VANILLA,
        starting_location=StartingLocation.default(),
    )
Esempio n. 17
0
def static_resources_for_layout_logic(
    configuration: TrickLevelConfiguration,
    resource_database: ResourceDatabase,
) -> Tuple[int, CurrentResources]:
    """
    :param configuration:
    :param resource_database:
    :return:
    """

    all_used_tricks = TrickLevelConfiguration.all_possible_tricks()
    static_resources = {}

    for trick in resource_database.trick:
        if trick.index in all_used_tricks:
            static_resources[trick] = configuration.level_for_trick(
                trick).as_number

    # Exclude from Room Rando
    static_resources[resource_database.get_by_type_and_index(
        ResourceType.TRICK, 18)] = 5

    return configuration.global_level.as_number, static_resources
Esempio n. 18
0
    def _create_open_map_tracker_actions(self):
        base_layout = self.preset_manager.default_preset.layout_configuration

        for trick_level in LayoutTrickLevel:
            if trick_level != LayoutTrickLevel.MINIMAL_RESTRICTIONS:
                action = QtWidgets.QAction(self)
                action.setText(trick_level.long_name)
                self.menu_map_tracker.addAction(action)

                configuration = dataclasses.replace(
                    base_layout,
                    trick_level_configuration=TrickLevelConfiguration(trick_level, {})
                )
                action.triggered.connect(partial(self.open_map_tracker, configuration))
Esempio n. 19
0
def test_encode_no_tricks_are_removed():
    from_json = TrickLevelConfiguration.from_json(
        {
            "minimal_logic": False,
            "specific_levels": {
                "Dash": "disabled"
            }
        },
        game=RandovaniaGame.PRIME2)

    encoded = bitpacking._pack_encode_results([
        (value_argument, value_format)
        for value_argument, value_format in from_json.bit_pack_encode({})
    ])

    assert encoded == b'\x00\x00\x00\x00'

    decoder = BitPackDecoder(encoded)
    decoded = TrickLevelConfiguration.bit_pack_unpack(decoder, {
        "reference":
        TrickLevelConfiguration(False, {}, RandovaniaGame.PRIME2),
    })

    assert decoded.specific_levels == {}
 def from_json_dict(cls, json_dict: dict) -> "LayoutConfiguration":
     return cls.from_params(
         trick_level_configuration=TrickLevelConfiguration.from_json(json_dict["trick_level"]),
         sky_temple_keys=LayoutSkyTempleKeyMode(json_dict["sky_temple_keys"]),
         elevators=LayoutElevators(json_dict["elevators"]),
         starting_location=StartingLocation.from_json(json_dict["starting_location"]),
         randomization_mode=RandomizationMode(json_dict["randomization_mode"]),
         major_items_configuration=MajorItemsConfiguration.from_json(
             json_dict["major_items_configuration"],
             default_prime2_item_database(),
         ),
         ammo_configuration=AmmoConfiguration.from_json(
             json_dict["ammo_configuration"],
             default_prime2_item_database(),
         ),
         translator_configuration=TranslatorConfiguration.from_json(json_dict["translator_configuration"]),
         hints=HintConfiguration.from_json(json_dict["hints"]),
         split_beam_ammo=json_dict["split_beam_ammo"],
     )
Esempio n. 21
0
def trick_resources_for_configuration(configuration: TrickLevelConfiguration,
                                      resource_database: ResourceDatabase,
                                      ) -> CurrentResources:
    """
    :param configuration:
    :param resource_database:
    :return:
    """

    static_resources = {}

    for trick in resource_database.trick:
        if configuration.minimal_logic:
            level = LayoutTrickLevel.MINIMAL_LOGIC
        else:
            level = configuration.level_for_trick(trick)
        static_resources[trick] = level.as_number

    return static_resources
def test_reach_size_from_start(echoes_game_description):
    # Setup
    configuration = LayoutConfiguration.from_params(
        trick_level_configuration=TrickLevelConfiguration(
            LayoutTrickLevel.HYPERMODE), )
    patches = GamePatches.with_game(echoes_game_description)
    patches = patches.assign_gate_assignment(
        base_patches_factory.gate_assignment_for_configuration(
            configuration, echoes_game_description.resource_database,
            Random(15000)))

    game, state = logic_bootstrap(configuration, echoes_game_description,
                                  patches)

    # Run
    reach = GeneratorReach.reach_from_state(game, state)

    # Assert
    assert len(list(reach.nodes)) == 26
    assert len(list(reach.safe_nodes)) == 4
Esempio n. 23
0
def test_reach_size_from_start(echoes_game_description,
                               default_layout_configuration):
    # Setup
    layout_configuration = dataclasses.replace(
        default_layout_configuration,
        trick_level_configuration=TrickLevelConfiguration(
            LayoutTrickLevel.HYPERMODE),
    )
    player_pool = generator.create_player_pool(Random(15000),
                                               layout_configuration, 0)

    game, state = logic_bootstrap(layout_configuration, player_pool.game,
                                  player_pool.patches)

    # Run
    reach = GeneratorReach.reach_from_state(game, state)

    # Assert
    assert len(list(reach.nodes)) == 44
    assert len(list(reach.safe_nodes)) == 5
Esempio n. 24
0
def static_resources_for_layout_logic(
    configuration: TrickLevelConfiguration,
    resource_database: ResourceDatabase,
) -> CurrentResources:
    """
    :param configuration:
    :param resource_database:
    :return:
    """

    static_resources = {}

    for trick in resource_database.trick:
        static_resources[trick] = configuration.level_for_trick(
            trick).as_number

    # Room Rando
    room_rando = find_resource_info_with_long_name(resource_database.misc,
                                                   "Room Randomizer")
    static_resources[room_rando] = 0

    return static_resources
Esempio n. 25
0
def test_reach_size_from_start(echoes_game_description,
                               default_layout_configuration):
    # Setup
    configuration = dataclasses.replace(
        default_layout_configuration,
        trick_level_configuration=TrickLevelConfiguration(
            LayoutTrickLevel.HYPERMODE),
    )
    patches = echoes_game_description.create_game_patches()
    patches = patches.assign_gate_assignment(
        base_patches_factory.gate_assignment_for_configuration(
            configuration, echoes_game_description.resource_database,
            Random(15000)))

    game, state = logic_bootstrap(configuration, echoes_game_description,
                                  patches)

    # Run
    reach = GeneratorReach.reach_from_state(game, state)

    # Assert
    assert len(list(reach.nodes)) == 25
    assert len(list(reach.safe_nodes)) == 4
Esempio n. 26
0
def _configuration_with_data(request, mocker, echoes_game_description):
    tricks = echoes_game_description.resource_database.trick[:14]
    mocker.patch("randovania.layout.trick_level._all_tricks",
                 return_value=tricks)
    return request.param["encoded"], TrickLevelConfiguration.from_json(
        request.param["json"])
Esempio n. 27
0
def test_pretty_description_tricks_echoes(levels, expected):
    config = TrickLevelConfiguration(False, levels, RandovaniaGame.PRIME2)
    assert config.pretty_description == expected
Esempio n. 28
0
    with pytest.raises(ValueError):
        Permalink.from_str(invalid)


@pytest.mark.parametrize("spoiler", [False, True])
@pytest.mark.parametrize("patcher", [
    PatcherConfiguration.default(),
    PatcherConfiguration(
        menu_mod=True,
        warp_to_start=False,
    ),
])
@pytest.mark.parametrize("layout", [
    LayoutConfiguration.default(),
    LayoutConfiguration.from_params(
        trick_level_configuration=TrickLevelConfiguration(
            LayoutTrickLevel.HARD),
        sky_temple_keys=LayoutSkyTempleKeyMode.ALL_GUARDIANS,
        elevators=LayoutElevators.RANDOMIZED,
    ),
])
def test_round_trip(spoiler: bool, patcher: PatcherConfiguration,
                    layout: LayoutConfiguration):
    # Setup
    link = Permalink(
        seed_number=1000,
        spoiler=spoiler,
        patcher_configuration=patcher,
        layout_configuration=layout,
    )

    # Run
Esempio n. 29
0
def _configuration_with_data(request):
    return request.param["encoded"], TrickLevelConfiguration.from_json(request.param["json"])
    def setup_trick_level_elements(self):
        # logic_combo_box
        for i, trick_level in enumerate(LayoutTrickLevel):
            self.logic_combo_box.setItemData(i, trick_level)

        self.logic_combo_box.currentIndexChanged.connect(self._on_trick_level_changed)

        self.trick_difficulties_layout = QtWidgets.QGridLayout()
        self._checkbox_for_trick = {}
        self._slider_for_trick = {}

        configurable_tricks = TrickLevelConfiguration.all_possible_tricks()
        used_tricks = _used_tricks(self.world_list)

        size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)

        self._create_difficulty_details_row()

        row = 2
        for trick in sorted(self.resource_database.trick, key=lambda _trick: _trick.long_name):
            if trick.index not in configurable_tricks or trick not in used_tricks:
                continue

            if row > 1:
                self.trick_difficulties_layout.addItem(QtWidgets.QSpacerItem(20, 40,
                                                                             QtWidgets.QSizePolicy.Minimum,
                                                                             QtWidgets.QSizePolicy.Expanding))

            trick_configurable = QtWidgets.QCheckBox(self.trick_level_scroll_contents)
            trick_configurable.setFixedWidth(16)
            trick_configurable.stateChanged.connect(functools.partial(self._on_check_trick_configurable, trick))
            self._checkbox_for_trick[trick] = trick_configurable
            self.trick_difficulties_layout.addWidget(trick_configurable, row, 0, 1, 1)

            trick_label = QtWidgets.QLabel(self.trick_level_scroll_contents)
            trick_label.setSizePolicy(size_policy)
            trick_label.setWordWrap(True)
            trick_label.setFixedWidth(80)
            trick_label.setText(trick.long_name)

            self.trick_difficulties_layout.addWidget(trick_label, row, 1, 1, 1)

            slider_layout = QtWidgets.QGridLayout()
            slider_layout.setHorizontalSpacing(0)
            for i in range(12):
                slider_layout.setColumnStretch(i, 1)

            horizontal_slider = QtWidgets.QSlider(self.trick_level_scroll_contents)
            horizontal_slider.setMaximum(5)
            horizontal_slider.setPageStep(2)
            horizontal_slider.setOrientation(QtCore.Qt.Horizontal)
            horizontal_slider.setTickPosition(QtWidgets.QSlider.TicksBelow)
            horizontal_slider.setEnabled(False)
            horizontal_slider.valueChanged.connect(functools.partial(self._on_slide_trick_slider, trick))
            self._slider_for_trick[trick] = horizontal_slider
            slider_layout.addWidget(horizontal_slider, 0, 1, 1, 10)

            difficulties_for_trick = _difficulties_for_trick(self.world_list, trick)
            for i, trick_level in enumerate(LayoutTrickLevel):
                if trick_level == LayoutTrickLevel.NO_TRICKS or trick_level in difficulties_for_trick:
                    difficulty_label = QtWidgets.QLabel(self.trick_level_scroll_contents)
                    difficulty_label.setAlignment(QtCore.Qt.AlignHCenter)
                    difficulty_label.setText(trick_level.long_name)

                    slider_layout.addWidget(difficulty_label, 1, 2 * i, 1, 2)

            self.trick_difficulties_layout.addLayout(slider_layout, row, 2, 1, 1)

            tool_button = QtWidgets.QToolButton(self.trick_level_scroll_contents)
            tool_button.setText("?")
            tool_button.clicked.connect(functools.partial(self._open_trick_details_popup, trick))
            self.trick_difficulties_layout.addWidget(tool_button, row, 3, 1, 1)

            row += 1

        self.trick_level_layout.addLayout(self.trick_difficulties_layout)