Beispiel #1
0
    async def add_commands(self, slash: SlashCommand):
        slash.add_slash_command(
            self.database_command,
            name=self.configuration.get("command_prefix", "") +
            "database-inspect",
            description=
            "Consult the Randovania's logic database for one specific room.",
            guild_ids=None,
            options=[
                manage_commands.create_option(
                    "game",
                    "The game's database to check.",
                    option_type=SlashCommandOptionType.STRING,
                    required=True,
                    choices=[
                        manage_commands.create_choice(game.value,
                                                      game.long_name)
                        for game in enum_lib.iterate_enum(RandovaniaGame)
                    ])
            ],
        )

        def add_id(custom_id: str, call, **kwargs):
            self._on_database_component_listener[
                custom_id] = functools.partial(call, **kwargs)

        for game in enum_lib.iterate_enum(RandovaniaGame):
            db = default_database.game_description_for(game)
            world_options = await create_split_worlds(db)
            self._split_worlds[game] = world_options

            add_id(f"{game.value}_world",
                   self.on_database_world_selected,
                   game=game)
            add_id(f"back_to_{game.value}",
                   self.on_database_back_to_game,
                   game=game)

            for i, split_world in enumerate(world_options):
                add_id(f"{game.value}_world_{i}",
                       self.on_database_area_selected,
                       game=game,
                       split_world=split_world,
                       world_id=i)
                for j, area in enumerate(split_world.areas):
                    add_id(f"{game.value}_world_{i}_area_{j}",
                           self.on_area_node_selection,
                           game=game,
                           area=area)

        slash.add_component_callback(
            self.on_database_component,
            components=list(self._on_database_component_listener.keys()),
            use_callback_name=False,
        )
Beispiel #2
0
    def _setup_difficulties_menu(self, game: RandovaniaGame,
                                 menu: QtWidgets.QMenu):
        from randovania.game_description import default_database
        game = default_database.game_description_for(game)
        tricks_in_use = used_tricks(game)

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

            trick_menu = QtWidgets.QMenu(self)
            trick_menu.setTitle(_t(trick.long_name))
            menu.addAction(trick_menu.menuAction())

            used_difficulties = difficulties_for_trick(game, trick)
            for trick_level in enum_lib.iterate_enum(LayoutTrickLevel):
                if trick_level in used_difficulties:
                    difficulty_action = QtGui.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, game,
                                          trick, trick_level))
Beispiel #3
0
def create_subparsers(sub_parsers):
    parser: ArgumentParser = sub_parsers.add_parser(
        "database", help="Actions for database manipulation")

    group = parser.add_mutually_exclusive_group()
    group.add_argument(
        "--game",
        type=str,
        choices=[game.value for game in iterate_enum(RandovaniaGame)],
        default=RandovaniaGame.PRIME2.value,
        help="Use the included database for the given game.",
    )
    group.add_argument(
        "--json-database",
        type=Path,
        help="Path to the JSON encoded database.",
    )

    sub_parsers = parser.add_subparsers(dest="database_command")
    create_convert_database_command(sub_parsers)
    view_area_command(sub_parsers)
    export_areas_command(sub_parsers)
    list_paths_with_dangerous_command(sub_parsers)
    list_paths_with_resource_command(sub_parsers)
    pickups_per_area_command(sub_parsers)

    def check_command(args):
        if args.database_command is None:
            parser.print_help()
            raise SystemExit(1)

    parser.set_defaults(func=check_command)
Beispiel #4
0
def write_human_readable_world_list(game: GameDescription,
                                    output: TextIO) -> None:
    def print_to_file(*args):
        output.write("\t".join(str(arg) for arg in args) + "\n")

    output.write("====================\nTemplates\n")
    for template_name, template in game.resource_database.requirement_template.items(
    ):
        output.write(f"\n* {template_name}:\n")
        for level, text in pretty_print_requirement(template):
            output.write("      {}{}\n".format("    " * level, text))

    output.write("\n====================\nDock Weaknesses\n")
    for dock_type in iterate_enum(DockType):
        output.write(f"\n> {dock_type}")
        for weakness in game.dock_weakness_database.get_by_type(dock_type):
            output.write(
                f"\n  * ({weakness.index}) {weakness.name}; Blast Shield? {weakness.is_blast_shield}\n"
            )
            for level, text in pretty_print_requirement(weakness.requirement):
                output.write("      {}{}\n".format("    " * level, text))

    output.write("\n")
    for world in game.world_list.worlds:
        output.write("====================\n{}\n".format(world.name))
        for area in world.areas:
            output.write("----------------\n")
            pretty_print_area(game, area, print_function=print_to_file)
    def pretty_description(self) -> str:
        if self.minimal_logic:
            return "Minimal Logic"

        count_at_difficulties = collections.defaultdict(list)
        for trick in _all_tricks(
                default_database.resource_database_for(self.game)):
            count_at_difficulties[self.level_for_trick(trick)].append(
                trick.long_name)

        if len(count_at_difficulties) == 1:
            for level in count_at_difficulties.keys():
                if level == LayoutTrickLevel.DISABLED:
                    return "All tricks disabled"
                return f"All tricks enabled at {level.long_name}"

        def tricks_at_level(tricks: List[str]) -> str:
            if len(tricks) != 1:
                return f"{len(tricks)}"
            else:
                return tricks[0]

        descriptions = [
            f"{tricks_at_level(count_at_difficulties[level])} at {level.long_name}"
            for level in iterate_enum(LayoutTrickLevel)
            if count_at_difficulties[level]
        ]
        return "Enabled tricks: {}".format(", ".join(descriptions))
    def __init__(self, editor: PresetEditor):
        super().__init__(editor)
        self.setupUi(self)

        self.translators_layout.setAlignment(QtCore.Qt.AlignTop)
        self.translator_randomize_all_button.clicked.connect(
            self._on_randomize_all_gates_pressed)
        self.translator_randomize_all_with_unlocked_button.clicked.connect(
            self._on_randomize_all_gates_with_unlocked_pressed)
        self.translator_vanilla_actual_button.clicked.connect(
            self._on_vanilla_actual_gates_pressed)
        self.translator_vanilla_colors_button.clicked.connect(
            self._on_vanilla_colors_gates_pressed)

        self._combo_for_gate = {}

        gate_index_to_name, identifier_to_gate = gate_data()

        for i, (identifier, gate_index) in enumerate(
                sorted(identifier_to_gate.items(), key=lambda it: it[1])):
            label = QtWidgets.QLabel(self.translators_scroll_contents)
            label.setText(gate_index_to_name[gate_index])
            self.translators_layout.addWidget(label, 3 + i, 0, 1, 1)

            combo = QComboBox(self.translators_scroll_contents)
            combo.identifier = identifier
            for item in iterate_enum(LayoutTranslatorRequirement):
                combo.addItem(item.long_name, item)
            combo.currentIndexChanged.connect(
                functools.partial(self._on_gate_combo_box_changed, combo))

            self.translators_layout.addWidget(combo, 3 + i, 1, 1, 2)
            self._combo_for_gate[combo.identifier] = combo
Beispiel #7
0
    def __init__(self):
        super().__init__()
        self.logger.setLevel(logging.DEBUG)
        self.window = QMainWindow()
        self.setupUi(self.window)
        common_qt_lib.set_default_window_icon(self.window)

        for status in enum_lib.iterate_enum(GameConnectionStatus):
            self.current_status_combo.addItem(status.pretty_text, status)

        self.permanent_pickups = []
        self.pickups = []

        self.collect_location_combo.setVisible(False)
        self.setup_collect_location_combo_button = QtWidgets.QPushButton(
            self.window)
        self.setup_collect_location_combo_button.setText(
            "Load list of locations")
        self.setup_collect_location_combo_button.clicked.connect(
            self._setup_locations_combo)
        self.gridLayout.addWidget(self.setup_collect_location_combo_button, 1,
                                  0, 1, 1)

        self.collect_location_button.clicked.connect(self._emit_collection)
        self.collect_location_button.setEnabled(False)

        self._expected_patches = dol_patcher.ALL_VERSIONS_PATCHES[0]
        # FIXME: use PAL again
        self.patches = self._expected_patches

        self._game_memory = bytearray(24 * (2**20))
        self._game_memory_initialized = False
        self.patches = None
Beispiel #8
0
def _create_resource_type_combo(
        current_resource_type: ResourceType, parent: QWidget,
        resource_database: ResourceDatabase) -> QComboBox:
    """

    :param current_resource_type:
    :param parent:
    :return:
    """
    resource_type_combo = QComboBox(parent)

    for resource_type in iterate_enum(ResourceType):
        try:
            count_elements = len(resource_database.get_by_type(resource_type))
        except ValueError:
            count_elements = 0

        if count_elements == 0:
            continue

        resource_type_combo.addItem(resource_type.name, resource_type)
        if resource_type is current_resource_type:
            resource_type_combo.setCurrentIndex(resource_type_combo.count() -
                                                1)

    return resource_type_combo
Beispiel #9
0
    async def add_commands(self, slash: SlashCommand):
        base_command = self.configuration.get("command_prefix",
                                              "") + "randovania-faq"

        for game in enum_lib.iterate_enum(RandovaniaGame):
            faq_entries = list(game.data.faq)
            if not faq_entries:
                continue

            def _shorten(n: str) -> str:
                if len(n) > 100:
                    return n[:97] + "..."
                return n

            slash.add_subcommand(
                functools.partial(self.faq_game_command, game=game),
                base_command,
                name=game.value,
                description=f"Prints the answer to a FAQ for {game.long_name}.",
                options=[
                    manage_commands.create_option(
                        "question",
                        "Which question to answer?",
                        option_type=SlashCommandOptionType.STRING,
                        required=True,
                        choices=[
                            manage_commands.create_choice(
                                f"question_{question_id}", _shorten(question))
                            for question_id, (question,
                                              answer) in enumerate(faq_entries)
                        ])
                ],
            )
    def __init__(self, editor: PresetEditor):
        super().__init__()
        self.setupUi(self)
        self._editor = editor

        randomizer_data = randovania.games.patchers.claris_patcher.decode_randomizer_data(
        )

        self.translators_layout.setAlignment(QtCore.Qt.AlignTop)
        self.translator_randomize_all_button.clicked.connect(
            self._on_randomize_all_gates_pressed)
        self.translator_vanilla_actual_button.clicked.connect(
            self._on_vanilla_actual_gates_pressed)
        self.translator_vanilla_colors_button.clicked.connect(
            self._on_vanilla_colors_gates_pressed)

        self._combo_for_gate = {}

        for i, gate in enumerate(randomizer_data["TranslatorLocationData"]):
            label = QtWidgets.QLabel(self.translators_scroll_contents)
            label.setText(gate["Name"])
            self.translators_layout.addWidget(label, 3 + i, 0, 1, 1)

            combo = QComboBox(self.translators_scroll_contents)
            combo.gate = TranslatorGate(gate["Index"])
            for item in iterate_enum(LayoutTranslatorRequirement):
                combo.addItem(item.long_name, item)
            combo.currentIndexChanged.connect(
                functools.partial(self._on_gate_combo_box_changed, combo))

            self.translators_layout.addWidget(combo, 3 + i, 1, 1, 2)
            self._combo_for_gate[combo.gate] = combo
Beispiel #11
0
def read_preset_list() -> List[Path]:
    preset_list = []
    for game in enum_lib.iterate_enum(RandovaniaGame):
        base_path = game.data_path.joinpath("presets")
        preset_list.extend([
            base_path.joinpath(preset["path"]) for preset in game.data.presets
        ])

    return preset_list
def refresh_presets_command_logic(args):
    for game in enum_lib.iterate_enum(RandovaniaGame):
        logging.info(f"Refreshing presets for {game.long_name}")
        base_path = game.data_path.joinpath("presets")

        for preset_relative_path in game.data.presets:
            preset_path = base_path.joinpath(preset_relative_path["path"])
            preset = VersionedPreset.from_file_sync(preset_path)
            preset.ensure_converted()
            preset.save_to_file(preset_path)
Beispiel #13
0
async def main():
    package_folder = Path("dist", "randovania")
    if package_folder.exists():
        shutil.rmtree(package_folder, ignore_errors=False)

    app_folder = Path("dist", "Randovania.app")
    if app_folder.exists():
        shutil.rmtree(app_folder, ignore_errors=False)

    for game in iterate_enum(RandovaniaGame):
        database.export_as_binary(
            default_data.read_json_then_binary(game)[1],
            _ROOT_FOLDER.joinpath("randovania", "data", "binary_data",
                                  f"{game.value}.bin"))

    if is_production():
        server_suffix = "randovania"
        client_id = 618134325921316864
    else:
        server_suffix = "randovania-staging"
        client_id = 887825192208969828

    configuration = {
        "discord_client_id": client_id,
        "server_address":
        f"https://randovania.metroidprime.run/{server_suffix}",
        "socketio_path": f"/{server_suffix}/socket.io",
    }
    with _ROOT_FOLDER.joinpath(
            "randovania", "data",
            "configuration.json").open("w") as config_release:
        json.dump(configuration, config_release)

    await download_nintendont()

    # HACK: pyintaller calls lipo/codesign on macOS and frequently timeout in github actions
    # There's also timeouts on Windows so we're expanding this to everyone
    print("Will patch timeout in PyInstaller compat")
    import PyInstaller.compat
    compat_path = Path(PyInstaller.compat.__file__)
    compat_text = compat_path.read_text().replace("timeout=60", "timeout=180")
    compat_path.write_text(compat_text)

    subprocess.run([sys.executable, "-m", "PyInstaller", "randovania.spec"],
                   check=True)

    if platform.system() == "Windows":
        create_windows_zip(package_folder)
    elif platform.system() == "Darwin":
        create_macos_zip(app_folder)
    elif platform.system() == "Linux":
        create_linux_zip(package_folder)
    else:
        raise ValueError(f"Unknown system: {platform.system()}")
Beispiel #14
0
    def __init__(self, editor: PresetEditor, game: GameDescription):
        super().__init__(editor)
        self.setupUi(self)
        self.game_description = game

        self.elevator_layout.setAlignment(QtCore.Qt.AlignTop)

        for value in enum_lib.iterate_enum(TeleporterShuffleMode):
            self.elevators_combo.addItem(value.long_name, value)
        self.elevators_combo.currentIndexChanged.connect(
            self._update_elevator_mode)
        signal_handling.on_checked(self.skip_final_bosses_check,
                                   self._update_require_final_bosses)
        signal_handling.on_checked(self.elevators_allow_unvisited_names_check,
                                   self._update_allow_unvisited_names)

        # Elevator Source
        self._create_source_elevators()

        # Elevator Target
        self._elevator_target_for_world, self._elevator_target_for_area = self.create_area_list_selection(
            self.elevators_target_group,
            self.elevators_target_layout,
            TeleporterTargetList.areas_list(self.game_enum),
            self._on_elevator_target_check_changed,
        )

        if self.game_enum != RandovaniaGame.METROID_PRIME_ECHOES:
            self.elevators_help_sound_bug_label.setVisible(False)
            self.elevators_allow_unvisited_names_check.setVisible(False)
            self.elevators_line_3.setVisible(False)
            self.elevators_help_list_label.setVisible(False)

        if self.game_enum == RandovaniaGame.METROID_PRIME:
            self.skip_final_bosses_check.setText(
                "Go directly to credits from Artifact Temple")
            self.skip_final_bosses_label.setText("""<html><head/><body>
            <p>Change the teleport in Artifact Temple to go directly to the credits, skipping the final bosses.</p>
            <p>This changes the requirements to <span style=" font-weight:600;">not need the final bosses</span>,
            turning certain items optional such as Plasma Beam.</p></body></html>
            """)
        elif self.game_enum != RandovaniaGame.METROID_PRIME_ECHOES:
            self.skip_final_bosses_check.setVisible(False)
            self.skip_final_bosses_label.setVisible(False)

        elif self.game_enum == RandovaniaGame.METROID_PRIME_CORRUPTION:
            self.elevators_description_label.setText(
                self.elevators_description_label.text().replace(
                    "elevator", "teleporter"))
Beispiel #15
0
    def __init__(
        self,
        parent: QWidget,
        layout: QHBoxLayout,
        resource_database: ResourceDatabase,
        item: ResourceRequirement,
    ):
        self.parent = parent
        self.layout = layout
        self.resource_database = resource_database

        self.resource_type_combo = _create_resource_type_combo(
            item.resource.resource_type, parent, resource_database)
        self.resource_type_combo.setMinimumWidth(75)
        self.resource_type_combo.setMaximumWidth(75)

        self.resource_name_combo = _create_resource_name_combo(
            self.resource_database, item.resource.resource_type, item.resource,
            self.parent)

        self.negate_combo = ScrollProtectedComboBox(parent)
        self.negate_combo.addItem("≥", False)
        self.negate_combo.addItem("<", True)
        self.negate_combo.setCurrentIndex(int(item.negate))
        self.negate_combo.setMinimumWidth(40)
        self.negate_combo.setMaximumWidth(40)

        self.negate_check = QtWidgets.QCheckBox(parent)
        self.negate_check.setChecked(item.negate)

        self.amount_edit = QLineEdit(parent)
        self.amount_edit.setValidator(QIntValidator(1, 10000))
        self.amount_edit.setText(str(item.amount))
        self.amount_edit.setMinimumWidth(45)
        self.amount_edit.setMaximumWidth(45)

        self.amount_combo = ScrollProtectedComboBox(parent)
        for trick_level in iterate_enum(LayoutTrickLevel):
            self.amount_combo.addItem(trick_level.long_name,
                                      userData=trick_level.as_number)
        self.amount_combo.setCurrentIndex(
            self.amount_combo.findData(item.amount))

        for widget in self._all_widgets:
            self.layout.addWidget(widget)

        self.resource_type_combo.currentIndexChanged.connect(self._update_type)
        self._update_visible_elements_by_type()
Beispiel #16
0
    def pretty_description(self) -> str:
        if self.minimal_logic:
            return "Minimal Logic"

        difficulties = collections.defaultdict(int)
        for trick in _all_tricks(
                default_database.resource_database_for(self.game)):
            difficulties[self.level_for_trick(trick)] += 1

        if len(difficulties) == 1:
            for level in difficulties.keys():
                return f"All at {level.long_name}"

        descriptions = [
            f"{difficulties[level]} at {level.long_name}"
            for level in iterate_enum(LayoutTrickLevel)
            if difficulties[level] > 0
        ]
        return ", ".join(descriptions)
Beispiel #17
0
    def _create_categories_boxes(self, item_database: ItemDatabase, size_policy):
        self._boxes_for_category = {}

        categories = set()
        for major_item in item_database.major_items.values():
            if not major_item.required:
                categories.add(major_item.item_category)

        all_categories = list(iterate_enum(ItemCategory))
        for major_item_category in sorted(categories, key=lambda it: all_categories.index(it)):
            category_box = QGroupBox(self.scroll_area_contents)
            category_box.setTitle(major_item_category.long_name)
            category_box.setSizePolicy(size_policy)
            category_box.setObjectName(f"category_box {major_item_category}")

            category_layout = QGridLayout(category_box)
            category_layout.setObjectName(f"category_layout {major_item_category}")

            self.item_pool_layout.addWidget(category_box)
            self._boxes_for_category[major_item_category] = category_box, category_layout, {}
Beispiel #18
0
def refresh_all_logic(args):
    from randovania.game_description import pretty_print
    from randovania.game_description import data_reader, data_writer
    from randovania.game_description import integrity_check

    gd_per_game = {}
    path_per_game = {}
    idb_per_game = {}

    for game in iterate_enum(RandovaniaGame):
        logging.info("Reading %s", game.long_name)
        path, data = default_data.read_json_then_binary(game)
        path_per_game[game] = path
        gd = data_reader.decode_data(data)
        gd_per_game[game] = gd

        idb = default_database.item_database_for_game(game)
        idb_per_game[game] = idb

    should_stop = False
    if args.integrity_check:
        for game, gd in gd_per_game.items():
            errors = integrity_check.find_database_errors(gd)
            if errors:
                logging.warning("Integrity errors for %s:\n%s", game.long_name,
                                "\n".join(errors))
                if game.data.development_state.is_stable:
                    should_stop = True

    if should_stop:
        return

    for game, gd in gd_per_game.items():
        path = path_per_game[game]
        logging.info("Writing %s", game.long_name)
        new_data = data_writer.write_game_description(gd)
        data_writer.write_as_split_files(new_data, path)
        path.with_suffix("").mkdir(parents=True, exist_ok=True)
        pretty_print.write_human_readable_game(gd, path.with_suffix(""))

        default_database.write_item_database_for_game(idb_per_game[game], game)
Beispiel #19
0
    def __init__(self, parent: QWidget, item: MajorItem,
                 starting_state: MajorItemState,
                 resources_database: ResourceDatabase):
        super().__init__(parent)
        self.setupUi(self)
        self._item = item

        self.item_name_label.setText(item.name)

        # Apply transparency on the separator line
        transparent = QGraphicsOpacityEffect(self.separator_line)
        transparent.setOpacity(0.33)
        self.separator_line.setGraphicsEffect(transparent)
        self.separator_line.hide()

        for case in enum_lib.iterate_enum(MajorItemStateCase):
            if case == MajorItemStateCase.VANILLA and item.original_index is None:
                continue
            if case == MajorItemStateCase.STARTING_ITEM and len(
                    item.progression) > 1:
                continue
    async def assign_other_hints(self, patches: GamePatches,
                                 identifiers: list[NodeIdentifier],
                                 prefill: PreFillParams) -> GamePatches:
        all_hint_identifiers = [
            identifier for identifier in identifiers
            if identifier not in patches.hints
        ]
        prefill.rng.shuffle(all_hint_identifiers)

        # Dark Temple hints
        temple_hints = list(enum_lib.iterate_enum(HintDarkTemple))
        while all_hint_identifiers and temple_hints:
            identifier = all_hint_identifiers.pop()
            patches = patches.assign_hint(
                identifier,
                Hint(HintType.RED_TEMPLE_KEY_SET,
                     None,
                     dark_temple=temple_hints.pop(0)))
            identifiers.remove(identifier)

        return patches
Beispiel #21
0
async def main():
    package_folder = Path("dist", "randovania")
    if package_folder.exists():
        shutil.rmtree(package_folder, ignore_errors=False)

    app_folder = Path("dist", "Randovania.app")
    if app_folder.exists():
        shutil.rmtree(app_folder, ignore_errors=False)

    for game in iterate_enum(RandovaniaGame):
        prime_database.export_as_binary(
            default_data.read_json_then_binary(game)[1],
            _ROOT_FOLDER.joinpath("randovania", "data", "binary_data",
                                  f"{game.value}.bin"))

    if is_production():
        server_suffix = "randovania"
    else:
        server_suffix = "randovania-staging"
    configuration = {
        "discord_client_id": 618134325921316864,
        "server_address":
        f"https://randovania.metroidprime.run/{server_suffix}",
        "socketio_path": f"/{server_suffix}/socket.io",
    }
    with _ROOT_FOLDER.joinpath(
            "randovania", "data",
            "configuration.json").open("w") as config_release:
        json.dump(configuration, config_release)

    await download_nintendont()

    subprocess.run([sys.executable, "-m", "PyInstaller", "randovania.spec"],
                   check=True)

    if platform.system() == "Windows":
        create_windows_zip(package_folder)
    elif platform.system() == "Darwin":
        create_macos_zip(app_folder)
    def update_items(self):
        self.clear()

        tree_item = {}
        for game in enum_lib.iterate_enum(RandovaniaGame):
            root = QtWidgets.QTreeWidgetItem(self)
            root.setText(0, game.long_name)
            root.setExpanded(True)
            tree_item[game] = root

        self.preset_to_item = {}

        # Included presets
        for preset in self.window_manager.preset_manager.included_presets.values(
        ):
            item = QtWidgets.QTreeWidgetItem(tree_item[preset.game])
            item.setText(0, preset.name)
            item.setExpanded(True)
            item.setData(0, Qt.UserRole, preset.uuid)
            self.preset_to_item[preset.uuid] = item

        # Custom Presets
        for preset in self.window_manager.preset_manager.custom_presets.values(
        ):
            item = QtWidgets.QTreeWidgetItem(tree_item[preset.game])
            item.setText(0, preset.name)
            item.setData(0, Qt.UserRole, preset.uuid)
            self.preset_to_item[preset.uuid] = item

        # Set parents after, so don't have issues with order
        for preset in sorted(
                self.window_manager.preset_manager.custom_presets.values(),
                key=lambda it: it.name):
            if preset.base_preset_uuid in self.preset_to_item:
                tree_item[preset.game].removeChild(
                    self.preset_to_item[preset.uuid])
                self.preset_to_item[preset.base_preset_uuid].addChild(
                    self.preset_to_item[preset.uuid])
    def setup_elevator_elements(self):
        for value in iterate_enum(TeleporterShuffleMode):
            self.elevators_combo.addItem(value.long_name, value)
        self.elevators_combo.currentIndexChanged.connect(self._update_elevator_mode)
        signal_handling.on_checked(self.skip_final_bosses_check, self._update_require_final_bosses)
        signal_handling.on_checked(self.elevators_allow_unvisited_names_check, self._update_allow_unvisited_names)

        # Elevator Source
        self._create_source_elevators()

        # Elevator Target
        self._elevator_target_for_world, self._elevator_target_for_area = self._create_area_list_selection(
            self.elevators_target_group,
            self.elevators_target_layout,
            TeleporterTargetList.areas_list(self.game_enum),
            self._on_elevator_target_check_changed,
        )

        if self.game_enum == RandovaniaGame.PRIME3:
            self.patches_tab_widget.setTabText(self.patches_tab_widget.indexOf(self.elevator_tab),
                                               "Teleporters")
            self.elevators_description_label.setText(
                self.elevators_description_label.text().replace("elevator", "teleporter")
            )
Beispiel #24
0
import pytest
from PySide2.QtCore import Qt
from mock import patch, MagicMock, AsyncMock

from randovania.game_connection.connection_base import GameConnectionStatus, InventoryItem
from randovania.games.prime import dol_patcher
from randovania.gui.debug_backend_window import DebugBackendWindow
from randovania.lib.enum_lib import iterate_enum


@pytest.fixture(name="backend")
def debug_backend_window(skip_qtbot):
    return DebugBackendWindow()


@pytest.mark.parametrize("expected_status", iterate_enum(GameConnectionStatus))
def test_current_status(backend, expected_status):
    all_status = list(iterate_enum(GameConnectionStatus))

    backend.current_status_combo.setCurrentIndex(
        all_status.index(expected_status))
    assert backend.current_status == expected_status


@pytest.mark.asyncio
async def test_display_message(backend):
    backend.patches = dol_patcher.ALL_VERSIONS_PATCHES[0]

    message = "Foo"
    await backend._perform_single_memory_operations(
        backend._write_string_to_game_buffer(message))
    def setup_trick_level_elements(self):
        self.trick_level_minimal_logic_check.stateChanged.connect(self._on_trick_level_minimal_logic_check)

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

        tricks_in_use = used_tricks(self.game_description)

        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 not in tricks_in_use:
                continue

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

            trick_label = QtWidgets.QLabel(self.trick_level_scroll_contents)
            trick_label.setSizePolicy(size_policy)
            trick_label.setWordWrap(True)
            trick_label.setFixedWidth(100)
            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)

            used_difficulties = difficulties_for_trick(self.game_description, trick)
            for i, trick_level in enumerate(iterate_enum(LayoutTrickLevel)):
                if trick_level == LayoutTrickLevel.DISABLED or trick_level in used_difficulties:
                    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)

            if self._window_manager is not None:
                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)
Beispiel #26
0
    def __init__(self, parent: QtWidgets.QWidget, game: GameDescription):
        super().__init__(parent)
        set_default_window_icon(self)
        self.setWindowTitle("Layers")

        self.root_widget = QtWidgets.QScrollArea()
        self.root_layout = QtWidgets.QVBoxLayout(self.root_widget)
        self.root_widget.setWidgetResizable(True)
        self.setWidget(self.root_widget)

        self.contents_widget = QtWidgets.QWidget()
        self.contents_layout = QtWidgets.QVBoxLayout(self.contents_widget)
        self.root_widget.setWidget(self.contents_widget)

        self.title_label = QtWidgets.QLabel(self.contents_widget)
        self.title_label.setText("Select visible layers")
        self.contents_layout.addWidget(self.title_label)

        self.layer_checks = []
        for layer in game.layers:
            self.layer_checks.append(
                layer_check := QtWidgets.QCheckBox(self.contents_widget))
            layer_check.setText(layer)
            layer_check.setChecked(True)
            signal_handling.on_checked(layer_check,
                                       lambda it: self._notify_change())
            self.contents_layout.addWidget(layer_check)

        self.add_layer_button = QtWidgets.QPushButton(self.contents_widget)
        self.add_layer_button.setText("Add new layer")
        self.add_layer_button.setEnabled(False)
        self.add_layer_button.setToolTip("Not implemented")
        self.contents_layout.addWidget(self.add_layer_button)

        self.tricks_box = QtWidgets.QGroupBox(self.contents_widget)
        self.tricks_box.setTitle("Simplify connections with:")
        self.contents_layout.addWidget(self.tricks_box)
        self.tricks_layout = QtWidgets.QVBoxLayout(self.tricks_box)

        self.tricks = {}
        for trick in sorted(game.resource_database.trick,
                            key=lambda it: it.long_name):
            trick_layout = QtWidgets.QHBoxLayout()
            self.tricks_layout.addLayout(trick_layout)

            trick_check = QtWidgets.QCheckBox(self.tricks_box)
            trick_check.setText(trick.long_name)
            trick_layout.addWidget(trick_check)

            trick_combo = ScrollProtectedComboBox(self.tricks_box)
            trick_layout.addWidget(trick_combo)
            for trick_level in enum_lib.iterate_enum(LayoutTrickLevel):
                trick_combo.addItem(trick_level.long_name,
                                    userData=trick_level.as_number)
            signal_handling.on_combo(trick_combo,
                                     lambda it: self._notify_change())
            trick_combo.setEnabled(False)

            signal_handling.on_checked(trick_check, trick_combo.setEnabled)
            signal_handling.on_checked(trick_check,
                                       lambda it: self._notify_change())

            self.tricks[(trick, trick_check)] = trick_combo

        self.load_preset_button = QtWidgets.QPushButton(self.contents_widget)
        self.load_preset_button.setText("Configure with preset")
        self.load_preset_button.clicked.connect(self._on_load_preset_slot)
        self.contents_layout.addWidget(self.load_preset_button)

        self.vertical_spacer = QtWidgets.QSpacerItem(
            20, 30, QtWidgets.QSizePolicy.Minimum,
            QtWidgets.QSizePolicy.Expanding)
        self.contents_layout.addItem(self.vertical_spacer)
 def case(self) -> MajorItemStateCase:
     for case in enum_lib.iterate_enum(MajorItemStateCase):
         if self == MajorItemState.from_case(case, self.included_ammo):
             return case
def add_echoes_default_hints_to_patches(rng: Random,
                                        patches: GamePatches,
                                        world_list: WorldList,
                                        num_joke: int,
                                        is_multiworld: bool,
                                        ) -> GamePatches:
    """
    Adds hints that are present on all games.
    :param rng:
    :param patches:
    :param world_list:
    :param num_joke
    :param is_multiworld
    :return:
    """

    for node in world_list.all_nodes:
        if isinstance(node, LogbookNode) and node.lore_type == LoreType.LUMINOTH_WARRIOR:
            patches = patches.assign_hint(node.resource(),
                                          Hint(HintType.LOCATION,
                                               PrecisionPair(HintLocationPrecision.KEYBEARER,
                                                             HintItemPrecision.BROAD_CATEGORY,
                                                             include_owner=True),
                                               PickupIndex(node.hint_index)))

    all_logbook_assets = [node.resource()
                          for node in world_list.all_nodes
                          if isinstance(node, LogbookNode)
                          and node.resource() not in patches.hints
                          and node.lore_type.holds_generic_hint]

    rng.shuffle(all_logbook_assets)

    # The 4 guaranteed hints
    indices_with_hint = [
        (PickupIndex(24), HintLocationPrecision.LIGHT_SUIT_LOCATION),  # Light Suit
        (PickupIndex(43), HintLocationPrecision.GUARDIAN),  # Dark Suit (Amorbis)
        (PickupIndex(79), HintLocationPrecision.GUARDIAN),  # Dark Visor (Chykka)
        (PickupIndex(115), HintLocationPrecision.GUARDIAN),  # Annihilator Beam (Quadraxis)
    ]
    rng.shuffle(indices_with_hint)
    for index, location_type in indices_with_hint:
        if not all_logbook_assets:
            break

        logbook_asset = all_logbook_assets.pop()
        patches = patches.assign_hint(logbook_asset, Hint(HintType.LOCATION,
                                                          PrecisionPair(location_type, HintItemPrecision.DETAILED,
                                                                        include_owner=False),
                                                          index))

    # Dark Temple hints
    temple_hints = list(iterate_enum(HintDarkTemple))
    while all_logbook_assets and temple_hints:
        logbook_asset = all_logbook_assets.pop()
        patches = patches.assign_hint(logbook_asset, Hint(HintType.RED_TEMPLE_KEY_SET, None,
                                                          dark_temple=temple_hints.pop(0)))

    # Jokes
    while num_joke > 0 and all_logbook_assets:
        logbook_asset = all_logbook_assets.pop()
        patches = patches.assign_hint(logbook_asset, Hint(HintType.JOKE, None))
        num_joke -= 1

    return patches
Beispiel #29
0
    def __init__(self, game: GameDescription, node: Node):
        super().__init__()
        self.setupUi(self)
        common_qt_lib.set_default_window_icon(self)

        self.game = game
        self.node = node

        self._type_to_tab = {
            GenericNode: self.tab_generic,
            DockNode: self.tab_dock,
            PickupNode: self.tab_pickup,
            TeleporterNode: self.tab_teleporter,
            EventNode: self.tab_event,
            ConfigurableNode: self.tab_translator_gate,
            LogbookNode: self.tab_logbook,
            PlayerShipNode: self.tab_player_ship,
        }
        tab_to_type = {tab: node_type for node_type, tab in self._type_to_tab.items()}

        # Dynamic Stuff
        for i, node_type in enumerate(self._type_to_tab.keys()):
            self.node_type_combo.setItemData(i, node_type)

        self.layers_combo.clear()
        for layer in game.layers:
            self.layers_combo.addItem(layer)

        self.dock_type_combo.clear()
        for i, dock_type in enumerate(game.dock_weakness_database.dock_types):
            self.dock_type_combo.addItem(dock_type.long_name, userData=dock_type)
        refresh_if_needed(self.dock_type_combo, self.on_dock_type_combo)

        for world in sorted(game.world_list.worlds, key=lambda x: x.name):
            self.dock_connection_world_combo.addItem(world.name, userData=world)
            self.teleporter_destination_world_combo.addItem(world.name, userData=world)
        refresh_if_needed(self.teleporter_destination_world_combo, self.on_dock_connection_world_combo)
        refresh_if_needed(self.teleporter_destination_world_combo, self.on_teleporter_destination_world_combo)

        for event in sorted(game.resource_database.event, key=lambda it: it.long_name):
            self.event_resource_combo.addItem(event.long_name, event)
        if self.event_resource_combo.count() == 0:
            self.event_resource_combo.addItem("No events in database", None)
            self.event_resource_combo.setEnabled(False)

        for i, dock_type in enumerate(enum_lib.iterate_enum(LoreType)):
            self.lore_type_combo.setItemData(i, dock_type)
        refresh_if_needed(self.lore_type_combo, self.on_lore_type_combo)

        self.set_unlocked_by(Requirement.trivial())

        # Signals
        self.button_box.accepted.connect(self.try_accept)
        self.button_box.rejected.connect(self.reject)
        self.name_edit.textEdited.connect(self.on_name_edit)
        self.node_type_combo.currentIndexChanged.connect(self.on_node_type_combo)
        self.dock_connection_world_combo.currentIndexChanged.connect(self.on_dock_connection_world_combo)
        self.dock_connection_area_combo.currentIndexChanged.connect(self.on_dock_connection_area_combo)
        self.dock_type_combo.currentIndexChanged.connect(self.on_dock_type_combo)
        self.dock_update_name_button.clicked.connect(self.on_dock_update_name_button)
        self.teleporter_destination_world_combo.currentIndexChanged.connect(self.on_teleporter_destination_world_combo)
        self.lore_type_combo.currentIndexChanged.connect(self.on_lore_type_combo)
        self.player_ship_unlocked_button.clicked.connect(self.on_player_ship_unlocked_button)

        # Hide the tab bar
        tab_bar: QtWidgets.QTabBar = self.tab_widget.findChild(QtWidgets.QTabBar)
        tab_bar.hide()

        # Values
        self.name_edit.setText(node.name)
        self.heals_check.setChecked(node.heal)
        self.location_group.setChecked(node.location is not None)
        if node.location is not None:
            self.location_x_spin.setValue(node.location.x)
            self.location_y_spin.setValue(node.location.y)
            self.location_z_spin.setValue(node.location.z)
        self.description_edit.setMarkdown(node.description)
        self.extra_edit.setPlainText(json.dumps(frozen_lib.unwrap(node.extra), indent=4))

        try:
            visible_tab = self._fill_for_type(node)
            self.node_type_combo.setCurrentIndex(self.node_type_combo.findData(tab_to_type[visible_tab]))
            refresh_if_needed(self.node_type_combo, self.on_node_type_combo)
        except Exception:
            pass

        self.on_name_edit(self.name_edit.text())
Beispiel #30
0
def test_current_status(backend, expected_status):
    all_status = list(iterate_enum(GameConnectionStatus))

    backend.current_status_combo.setCurrentIndex(
        all_status.index(expected_status))
    assert backend.current_status == expected_status