Exemplo n.º 1
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)
Exemplo n.º 2
0
    def _create_categories_boxes(self, size_policy):
        self._boxes_for_category = {}

        current_row = 0
        for major_item_category in iterate_enum(ItemCategory):
            if not major_item_category.is_major_category and major_item_category != ItemCategory.ENERGY_TANK:
                continue

            category_button = QToolButton(self.major_items_box)
            category_button.setGeometry(QRect(20, 30, 24, 21))
            category_button.setText("+")

            category_label = QLabel(self.major_items_box)
            category_label.setSizePolicy(size_policy)
            category_label.setText(major_item_category.long_name)

            category_box = QGroupBox(self.major_items_box)
            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.major_items_layout.addWidget(category_button, 2 * current_row + 1, 0, 1, 1)
            self.major_items_layout.addWidget(category_label, 2 * current_row + 1, 1, 1, 1)
            self.major_items_layout.addWidget(category_box, 2 * current_row + 2, 0, 1, 2)
            self._boxes_for_category[major_item_category] = category_box, category_layout, {}

            category_button.clicked.connect(partial(_toggle_box_visibility, category_button, category_box))
            category_box.setVisible(False)
            current_row += 1
Exemplo n.º 3
0
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"))

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

    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)
Exemplo n.º 4
0
    def __init__(self, editor: PresetEditor):
        super().__init__()
        self.setupUi(self)
        self._editor = editor

        randomizer_data = default_data.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
Exemplo n.º 5
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 __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
Exemplo n.º 7
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(trick.long_name)
            menu.addAction(trick_menu.menuAction())

            used_difficulties = difficulties_for_trick(game, trick)
            for i, trick_level in enumerate(iterate_enum(LayoutTrickLevel)):
                if trick_level in used_difficulties:
                    difficulty_action = QtWidgets.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))
Exemplo n.º 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
    def __init__(self):
        super().__init__()
        self.window = QMainWindow()
        self.setupUi(self.window)
        common_qt_lib.set_default_window_icon(self.window)

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

        self.permanent_pickups = []
        self.pickups = []
        self._inventory = {}

        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)
Exemplo n.º 10
0
    def __init__(self):
        super().__init__()
        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[1]
        self._game_memory = bytearray(24 * (2**20))
        self._write_memory(self._expected_patches.build_string_address,
                           self._expected_patches.build_string)

        # CPlayerState
        self._write_memory(
            self._expected_patches.string_display.cstate_manager_global +
            0x150c, 0xA00000.to_bytes(4, "big"))
Exemplo n.º 11
0
    def setup_elevator_elements(self):
        for value in iterate_enum(LayoutElevators):
            self.elevators_combo.addItem(value.long_name, value)

        self.elevators_combo.options_field_name = "layout_configuration_elevators"
        self.elevators_combo.currentIndexChanged.connect(functools.partial(_update_options_by_value,
                                                                           self._editor,
                                                                           self.elevators_combo))

        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")
            )
Exemplo n.º 12
0
    def _create_open_map_tracker_actions(self):
        base_layout = self.preset_manager.default_preset.get_preset(
        ).layout_configuration

        for trick_level in iterate_enum(LayoutTrickLevel):
            if trick_level != LayoutTrickLevel.MINIMAL_LOGIC:
                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))
Exemplo n.º 13
0
    def pretty_description(self) -> str:
        if self.minimal_logic:
            return LayoutTrickLevel.MINIMAL_LOGIC.long_name

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

        if len(difficulties) == 1:
            for level in difficulties.keys():
                return level.long_name

        descriptions = [
            f"{difficulties[level]} at {level.long_name}"
            for level in iterate_enum(LayoutTrickLevel)
            if difficulties[level] > 0
        ]
        return ", ".join(descriptions)
Exemplo n.º 14
0
    def pretty_description(self) -> str:
        if self.minimal_logic:
            return "Minimal Logic"

        trick_list = _all_tricks(default_data.read_json_then_binary(self.game)[1])
        difficulties = collections.defaultdict(int)
        for trick in trick_list:
            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)
Exemplo n.º 15
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))

        current_row = 0
        for major_item_category in sorted(
                categories, key=lambda it: all_categories.index(it)):
            category_button = QToolButton(self.major_items_box)
            category_button.setGeometry(QRect(20, 30, 24, 21))
            category_button.setText("+")

            category_label = QLabel(self.major_items_box)
            category_label.setSizePolicy(size_policy)
            category_label.setText(major_item_category.long_name)

            category_box = QGroupBox(self.major_items_box)
            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.major_items_layout.addWidget(category_button,
                                              2 * current_row + 1, 0, 1, 1)
            self.major_items_layout.addWidget(category_label,
                                              2 * current_row + 1, 1, 1, 1)
            self.major_items_layout.addWidget(category_box,
                                              2 * current_row + 2, 0, 1, 2)
            self._boxes_for_category[
                major_item_category] = category_box, category_layout, {}

            category_button.clicked.connect(
                partial(_toggle_box_visibility, category_button, category_box))
            category_box.setVisible(False)
            current_row += 1
Exemplo n.º 16
0
    def _setup_difficulties_menu(self):
        game = default_database.default_prime2_game_description()
        tricks_in_use = used_tricks(game)

        for trick in sorted(game.resource_database.trick,
                            key=lambda _trick: _trick.long_name):
            if 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, trick)
            for i, trick_level in enumerate(iterate_enum(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))
Exemplo n.º 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, {}
Exemplo n.º 18
0

@pytest.fixture(name="pickup")
def _pickup(echoes_game_description) -> PickupEntry:
    resource = echoes_game_description.resource_database.energy_tank

    return PickupEntry(
        name="Pickup",
        model_index=0,
        item_category=ItemCategory.MOVEMENT,
        broad_category=ItemCategory.LIFE_SUPPORT,
        resources=(ConditionalResources(None, None, ((resource, 2), )), ),
    )


@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))
Exemplo n.º 19
0
    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.NO_TRICKS 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)
Exemplo n.º 20
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
Exemplo n.º 21
0
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
Exemplo n.º 22
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.world = game.world_list.nodes_to_world(node)
        world = self.world

        self._type_to_tab = {
            GenericNode: self.tab_generic,
            DockNode: self.tab_dock,
            PickupNode: self.tab_pickup,
            TeleporterNode: self.tab_teleporter,
            EventNode: self.tab_event,
            TranslatorGateNode: 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)

        for area in world.areas:
            self.dock_connection_area_combo.addItem(area.name, area)
        refresh_if_needed(self.dock_connection_area_combo,
                          self.on_dock_connection_area_combo)

        for i, enum in enumerate(enum_lib.iterate_enum(DockType)):
            self.dock_type_combo.setItemData(i, enum)

        for world in sorted(game.world_list.worlds, key=lambda x: x.name):
            self.teleporter_destination_world_combo.addItem(
                "{0.name} ({0.dark_name})".format(world), userData=world)
        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, enum in enumerate(enum_lib.iterate_enum(LoreType)):
            self.lore_type_combo.setItemData(i, enum)
        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.node_type_combo.currentIndexChanged.connect(
            self.on_node_type_combo)
        self.dock_connection_area_combo.currentIndexChanged.connect(
            self.on_dock_connection_area_combo)
        self.dock_connection_node_combo.currentIndexChanged.connect(
            self.on_dock_connection_node_combo)
        self.dock_type_combo.currentIndexChanged.connect(
            self.on_dock_type_combo)
        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)

        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)