Exemplo n.º 1
0
def test_create_claris_patcher_file(test_files_dir, randomizer_data):
    # Setup
    description = LayoutDescription.from_file(
        test_files_dir.joinpath("log_files", "seed_a.rdvgame"))
    player_index = 0
    preset = description.permalink.get_preset(player_index)
    cosmetic_patches = CosmeticPatches()

    # Run
    result = claris_patcher_file.create_patcher_file(
        description, PlayersConfiguration(player_index, {0: "you"}),
        cosmetic_patches, randomizer_data)

    # Assert
    assert isinstance(result["spawn_point"], dict)

    assert isinstance(result["pickups"], list)
    assert len(result["pickups"]) == 119

    assert isinstance(result["elevators"], list)
    assert len(result["elevators"]) == 22

    assert isinstance(result["translator_gates"], list)
    assert len(result["translator_gates"]) == 17

    assert isinstance(result["string_patches"], list)
    assert len(result["string_patches"]) == 60

    assert result["specific_patches"] == {
        "hive_chamber_b_post_state": True,
        "intro_in_post_state": True,
        "warp_to_start": preset.configuration.warp_to_start,
        "speed_up_credits": cosmetic_patches.speed_up_credits,
        "disable_hud_popup": cosmetic_patches.disable_hud_popup,
        "pickup_map_icons": cosmetic_patches.pickup_markers,
        "full_map_at_start": cosmetic_patches.open_map,
        "dark_world_varia_suit_damage": preset.configuration.varia_suit_damage,
        "dark_world_dark_suit_damage": preset.configuration.dark_suit_damage,
        "always_up_gfmc_compound": True,
        "always_up_torvus_temple": True,
        "always_up_great_temple": False,
    }
    # TODO: check all fields?
    assert result["dol_patches"]["default_items"] == {
        "visor": "Combat Visor",
        "beam": "Power Beam",
    }
Exemplo n.º 2
0
def test_multi_create_pickup_data_for_self(pickup_for_create_pickup_data):
    # Setup
    creator = patcher_file.PickupCreatorMulti(
        MagicMock(), patcher_file._SimplifiedMemo(),
        PlayersConfiguration(0, {
            0: "You",
            1: "Someone"
        }))

    # Run
    data = creator.create_pickup_data(
        PickupIndex(10), PickupTarget(pickup_for_create_pickup_data,
                                      0), pickup_for_create_pickup_data,
        PickupModelStyle.ALL_VISIBLE, "Scan Text")

    # Assert
    assert data == {
        'conditional_resources': [{
            'item':
            1,
            'resources': [
                {
                    'amount': 1,
                    'index': 2
                },
                {
                    'amount': 11,
                    'index': 74
                },
            ]
        }],
        'convert': [],
        'hud_text': ['Sugar acquired!', 'Salt acquired!'],
        'resources': [
            {
                'amount': 1,
                'index': 1
            },
            {
                'amount': 11,
                'index': 74
            },
        ],
        'scan':
        'Your Scan Text',
    }
Exemplo n.º 3
0
async def test_export_iso(skip_qtbot, mocker):
    # Setup
    mock_execute_dialog = mocker.patch("randovania.gui.lib.async_dialog.execute_dialog", new_callable=AsyncMock,
                                       return_value=QtWidgets.QDialog.Accepted)

    options = MagicMock()
    options.output_directory = None

    window_manager = MagicMock()

    window = GameDetailsWindow(window_manager, options)
    skip_qtbot.addWidget(window)
    window.layout_description = MagicMock()
    window._player_names = {}
    game = window.layout_description.get_preset.return_value.game
    game.exporter.is_busy = False
    window.layout_description.all_games = [game]
    patch_data = game.patch_data_factory.return_value.create_data.return_value

    players_config = PlayersConfiguration(
        player_index=window.current_player_index,
        player_names=window._player_names,
    )

    # Run
    await window._export_iso()

    # Assert
    game.patch_data_factory.assert_called_once_with(
        window.layout_description,
        players_config,
        options.options_for_game.return_value.cosmetic_patches
    )
    game.gui.export_dialog.assert_called_once_with(
        options,
        patch_data,
        window.layout_description.shareable_word_hash,
        window.layout_description.has_spoiler,
        [game]
    )
    mock_execute_dialog.assert_awaited_once_with(game.gui.export_dialog.return_value)
    game.exporter.export_game.assert_called_once_with(
        patch_data,
        game.gui.export_dialog.return_value.get_game_export_params.return_value,
        progress_update=ANY,
    )
Exemplo n.º 4
0
def test_create_hints_item_location(empty_patches, blank_pickup, item,
                                    location, owner, is_multiworld):
    # Setup
    asset_id = 1000
    pickup_index = PickupIndex(50)
    logbook_node, _, world_list = _create_world_list(asset_id, pickup_index)
    players_config = PlayersConfiguration(
        player_index=0,
        player_names={
            i: f"Player {i +  1}"
            for i in range(int(is_multiworld) + 1)
        },
    )
    location_precision, determiner, item_name = item
    if owner and is_multiworld:
        determiner = "&push;&main-color=#d4cc33;Player 1&pop;'s"

    patches = dataclasses.replace(empty_patches,
                                  pickup_assignment={
                                      pickup_index:
                                      PickupTarget(blank_pickup, 0),
                                  },
                                  hints={
                                      logbook_node.resource():
                                      Hint(
                                          HintType.LOCATION,
                                          PrecisionPair(location[0],
                                                        location_precision,
                                                        include_owner=owner),
                                          pickup_index)
                                  })
    rng = MagicMock()

    # Run
    result = randovania.games.prime.patcher_file_lib.hints.create_hints(
        {0: patches}, players_config, world_list, rng)

    # Assert
    message = "{} {} can be found in {}.".format(determiner, item_name,
                                                 location[1])
    # message = "The Flying Ing Cache in {} contains {}.".format(location[1], item[1])
    assert result[0]['strings'][0] == message
    assert result == [{
        'asset_id': asset_id,
        'strings': [message, '', message]
    }]
Exemplo n.º 5
0
def test_create_string_patches(
    mock_stk_create_hints: MagicMock,
    mock_stk_hide_hints: MagicMock,
    mock_item_create_hints: MagicMock,
    mock_logbook_title_string_patches: MagicMock,
    stk_mode: SkyTempleKeyHintMode,
):
    # Setup
    game = MagicMock()
    all_patches = MagicMock()
    rng = MagicMock()
    mock_item_create_hints.return_value = ["item", "hints"]
    mock_stk_create_hints.return_value = ["show", "hints"]
    mock_stk_hide_hints.return_value = ["hide", "hints"]
    player_config = PlayersConfiguration(0, {0: "you"})
    mock_logbook_title_string_patches.return_values = []

    # Run
    result = patcher_file._create_string_patches(
        HintConfiguration(sky_temple_keys=stk_mode),
        game,
        all_patches,
        player_config,
        rng,
    )

    # Assert
    expected_result = ["item", "hints"]
    mock_item_create_hints.assert_called_once_with(all_patches, player_config,
                                                   game.world_list, rng)
    mock_logbook_title_string_patches.assert_called_once_with()

    if stk_mode == SkyTempleKeyHintMode.DISABLED:
        mock_stk_hide_hints.assert_called_once_with()
        mock_stk_create_hints.assert_not_called()
        expected_result.extend(["hide", "hints"])

    else:
        mock_stk_create_hints.assert_called_once_with(
            all_patches, player_config, game.world_list,
            stk_mode == SkyTempleKeyHintMode.HIDE_AREA)
        mock_stk_hide_hints.assert_not_called()
        expected_result.extend(["show", "hints"])

    assert result == expected_result
def test_patch_data_creation(test_files_dir):
    # Load the RDV game
    game_file = test_files_dir.joinpath("sdm_test_game.rdvgame").open()
    game_json = json.load(game_file)
    game_file.close()
    layout = LayoutDescription.from_json_dict(game_json)

    # Create patch_data from layout
    player_configuration = PlayersConfiguration(0, {0: "Player"})
    cosmetic_patches = SuperMetroidCosmeticPatches()

    patch_data = SuperMetroidPatchDataFactory(layout, player_configuration, cosmetic_patches).create_data()

    # Load control patch_data and compare the two for differences
    json_file = test_files_dir.joinpath("sdm_expected_result.json").open()
    expected_json = json.load(json_file)
    json_file.close()
    assert expected_json == patch_data
Exemplo n.º 7
0
def test_game_session_admin_player_patcher_file(
        mock_layout_description: PropertyMock, flask_app, clean_database):
    user1 = database.User.create(id=1234, name="The Name")
    user2 = database.User.create(id=1235, name="Brother")
    session = database.GameSession.create(id=1,
                                          name="Debug",
                                          state=GameSessionState.IN_PROGRESS,
                                          creator=user1)
    database.GameSessionPreset.create(session=session, row=0, preset="{}")
    database.GameSessionPreset.create(session=session, row=1, preset="{}")
    database.GameSessionPreset.create(session=session, row=2, preset="{}")
    database.GameSessionMembership.create(user=user1,
                                          session=session,
                                          row=2,
                                          admin=False)
    database.GameSessionMembership.create(user=user2,
                                          session=session,
                                          row=1,
                                          admin=False)
    sio = MagicMock()
    sio.get_current_user.return_value = user1
    patcher = sio.patcher_provider.patcher_for_game.return_value

    cosmetic = CosmeticPatches(open_map=False)

    # Run
    with flask_app.test_request_context():
        result = game_session.game_session_admin_player(
            sio, 1, 1234, "create_patcher_file", cosmetic.as_json)

    # Assert
    mock_layout_description.return_value.permalink.get_preset.assert_called_once_with(
        2)
    sio.patcher_provider.patcher_for_game.assert_called_once_with(
        mock_layout_description.return_value.permalink.get_preset.return_value.
        game)
    patcher.create_patch_data.assert_called_once_with(
        mock_layout_description.return_value,
        PlayersConfiguration(2, {
            0: "Player 1",
            1: "Brother",
            2: "The Name",
        }), cosmetic)
    assert result is patcher.create_patch_data.return_value
Exemplo n.º 8
0
def test_randomize_command_logic(mocker, with_permalink):
    mock_from_str = mocker.patch(
        "randovania.layout.permalink.Permalink.from_str")
    mock_from_file = mocker.patch(
        "randovania.layout.layout_description.LayoutDescription.from_file")
    mock_generate = mocker.patch(
        "randovania.generator.generator.generate_description")
    mock_apply = mocker.patch(
        "randovania.games.prime.claris_randomizer.apply_layout")

    args = MagicMock()
    if with_permalink:
        layout_description = mock_generate.return_value
    else:
        layout_description = mock_from_file.return_value
        args.permalink = None

    cosmetic_patches = CosmeticPatches(
        disable_hud_popup=args.disable_hud_popup,
        speed_up_credits=args.speed_up_credits)

    # Run
    randomize_command.randomize_command_logic(args)

    # Assert
    mock_apply.assert_called_once_with(description=layout_description,
                                       cosmetic_patches=cosmetic_patches,
                                       backup_files_path=args.backup_files,
                                       progress_update=ANY,
                                       game_root=args.game_files,
                                       players_config=PlayersConfiguration(
                                           0, {0: "You"}))

    if with_permalink:
        mock_from_str.assert_called_once_with(args.permalink)
        mock_generate.assert_called_once_with(
            permalink=mock_from_str.return_value,
            status_update=ANY,
            validate_after_generation=True)
        mock_from_file.assert_not_called()
    else:
        mock_from_str.assert_not_called()
        mock_generate.assert_not_called()
        mock_from_file.assert_called_once_with(args.log_file)
Exemplo n.º 9
0
def test_multi_create_pickup_data_for_other(pickup_for_create_pickup_data):
    # Setup
    creator = patcher_file.PickupCreatorMulti(MagicMock(), patcher_file._SimplifiedMemo(),
                                              PlayersConfiguration(0, {0: "You",
                                                                       1: "Someone"}))

    # Run
    data = creator.create_pickup_data(PickupIndex(10), PickupTarget(pickup_for_create_pickup_data, 1),
                                      pickup_for_create_pickup_data, PickupModelStyle.ALL_VISIBLE,
                                      "Scan Text")

    # Assert
    assert data == {
        'conditional_resources': [],
        'convert': [],
        'hud_text': ['Sent Cake to Someone!'],
        'resources': [{'amount': 11, 'index': 74}, ],
        'scan': "Someone's Scan Text",
    }
Exemplo n.º 10
0
async def patcher_data_command_logic_async(args):
    from randovania.interface_common.players_configuration import PlayersConfiguration
    from randovania.layout.layout_description import LayoutDescription

    layout_description = LayoutDescription.from_file(args.log_file)
    players_config = PlayersConfiguration(
        args.player_index,
        {i: f"Player {i + 1}"
         for i in range(layout_description.player_count)})
    preset = layout_description.get_preset(players_config.player_index)

    cosmetic_patches = preset.game.data.layout.cosmetic_patches.default()
    data_factory = preset.game.patch_data_factory(layout_description,
                                                  players_config,
                                                  cosmetic_patches)
    patch_data = data_factory.create_data()
    print(json.dumps(
        patch_data,
        indent=4,
    ))
Exemplo n.º 11
0
async def test_export_iso(skip_qtbot, mocker):
    # Setup
    mock_input_dialog = mocker.patch(
        "randovania.gui.seed_details_window.GameInputDialog")
    mock_execute_dialog = mocker.patch(
        "randovania.gui.lib.async_dialog.execute_dialog",
        new_callable=AsyncMock,
        return_value=QtWidgets.QDialog.Accepted)

    options = MagicMock()
    options.output_directory = None

    window_manager = MagicMock()
    patcher_provider = window_manager.patcher_provider
    patcher = patcher_provider.patcher_for_game.return_value
    patcher.is_busy = False

    window = SeedDetailsWindow(window_manager, options)
    window.layout_description = MagicMock()
    window._player_names = {}

    players_config = PlayersConfiguration(
        player_index=window.current_player_index,
        player_names=window._player_names,
    )

    # Run
    await window._export_iso()

    # Assert
    mock_execute_dialog.assert_awaited_once()
    patcher.create_patch_data.assert_called_once_with(
        window.layout_description, players_config, options.cosmetic_patches)
    patcher.patch_game.assert_called_once_with(
        mock_input_dialog.return_value.input_file,
        mock_input_dialog.return_value.output_file,
        patcher.create_patch_data.return_value,
        window._options.game_files_path,
        progress_update=ANY,
    )
Exemplo n.º 12
0
async def randomize_command_logic_async(args):
    from randovania.games.patcher_provider import PatcherProvider
    from randovania.generator import generator
    from randovania.interface_common.cosmetic_patches import CosmeticPatches
    from randovania.interface_common.players_configuration import PlayersConfiguration
    from randovania.layout.layout_description import LayoutDescription
    from randovania.layout.permalink import Permalink
    from randovania.interface_common.options import Options

    def status_update(s):
        if args.verbose:
            print(s)

    if args.permalink is not None:
        permalink = Permalink.from_str(args.permalink)
        layout_description = await generator.generate_and_validate_description(
            permalink=permalink,
            status_update=status_update,
            validate_after_generation=True,
        )
    else:
        layout_description = LayoutDescription.from_file(args.log_file)

    cosmetic_patches = CosmeticPatches(
        disable_hud_popup=args.disable_hud_popup,
        speed_up_credits=args.speed_up_credits)

    players_config = PlayersConfiguration(args.player_index,
                                          {i: f"Player {i + 1}"
                                           for i in range(layout_description.permalink.player_count)})
    preset = layout_description.permalink.get_preset(players_config.player_index)

    game_files_path = Options.with_default_data_dir().game_files_path
    patcher_provider = PatcherProvider()
    patcher = patcher_provider.patcher_for_game(preset.game)

    patch_data = patcher.create_patch_data(layout_description, players_config, cosmetic_patches)
    patcher.patch_game(args.input_file, args.output_file, patch_data, game_files_path, lambda x, _: status_update(x))
def test_create_patch_data(test_files_dir, mocker):
    # Setup
    file = test_files_dir.joinpath("log_files", "dread_1.rdvgame")
    description = LayoutDescription.from_file(file)
    players_config = PlayersConfiguration(0, {0: "Dread"})
    cosmetic_patches = DreadCosmeticPatches()
    mocker.patch(
        "randovania.layout.layout_description.LayoutDescription.shareable_word_hash",
        new_callable=PropertyMock,
        return_value="Words Hash")
    mocker.patch(
        "randovania.layout.layout_description.LayoutDescription.shareable_hash",
        new_callable=PropertyMock,
        return_value="$$$$$")

    # Run
    data = DreadPatchDataFactory(description, players_config,
                                 cosmetic_patches).create_data()

    # Expected Result
    with test_files_dir.joinpath("dread_expected_data.json").open("r") as file:
        expected_data = json.load(file)

    assert data == expected_data
Exemplo n.º 14
0
def randomize_command_logic(args):
    def status_update(s):
        if args.verbose:
            print(s)

    if args.permalink is not None:
        layout_description = generator.generate_description(
            permalink=Permalink.from_str(args.permalink),
            status_update=status_update,
            validate_after_generation=True)
    else:
        layout_description = LayoutDescription.from_file(args.log_file)

    cosmetic_patches = CosmeticPatches(
        disable_hud_popup=args.disable_hud_popup,
        speed_up_credits=args.speed_up_credits)

    claris_randomizer.apply_layout(
        description=layout_description,
        cosmetic_patches=cosmetic_patches,
        backup_files_path=args.backup_files,
        progress_update=lambda x, _: status_update(x),
        game_root=args.game_files,
        players_config=PlayersConfiguration(0, {0: "You"}))
Exemplo n.º 15
0
def game_session_admin_player(sio: ServerApp, session_id: int, user_id: int,
                              action: str, arg):
    _verify_has_admin(sio, session_id, user_id)
    action: SessionAdminUserAction = SessionAdminUserAction(action)

    session: GameSession = database.GameSession.get_by_id(session_id)
    membership = GameSessionMembership.get_by_ids(user_id, session_id)

    if action == SessionAdminUserAction.KICK:
        _add_audit_entry(
            sio, session, f"Kicked {membership.effective_name}"
            if membership.user != sio.get_current_user() else "Left session")
        membership.delete_instance()
        if not list(session.players):
            session.delete_instance(recursive=True)
            logger().info(
                f"{_describe_session(session)}. Kicking user {user_id} and deleting session."
            )
        else:
            logger().info(
                f"{_describe_session(session)}. Kicking user {user_id}.")

    elif action == SessionAdminUserAction.MOVE:
        offset: int = arg
        if membership.is_observer is None:
            raise InvalidAction("Player is an observer")

        new_row = membership.row + offset
        if new_row < 0:
            raise InvalidAction("New position is negative")
        if new_row >= session.num_rows:
            raise InvalidAction("New position is beyond num of rows")

        team_members = [None] * session.num_rows
        for member in GameSessionMembership.non_observer_members(session):
            team_members[member.row] = member

        while (0 <= new_row <
               session.num_rows) and team_members[new_row] is not None:
            new_row += offset

        if new_row < 0 or new_row >= session.num_rows:
            raise InvalidAction("No empty slots found in this direction")

        with database.db.atomic():
            logger().info(
                f"{_describe_session(session)}, User {user_id}. "
                f"Performing {action}, new row is {new_row}, from {membership.row}."
            )
            membership.row = new_row
            membership.save()

    elif action == SessionAdminUserAction.SWITCH_IS_OBSERVER:
        if membership.is_observer:
            membership.row = _find_empty_row(session)
        else:
            membership.row = None
        logger().info(
            f"{_describe_session(session)}, User {user_id}. Performing {action}, "
            f"new row is {membership.row}.")
        membership.save()

    elif action == SessionAdminUserAction.SWITCH_ADMIN:
        # Must be admin for this
        _verify_has_admin(sio, session_id, None, allow_when_no_admins=True)
        num_admins = GameSessionMembership.select().where(
            GameSessionMembership.session == session_id,
            GameSessionMembership.admin == True).count()

        if membership.admin and num_admins <= 1:
            raise InvalidAction("can't demote the only admin")

        membership.admin = not membership.admin
        _add_audit_entry(
            sio, session,
            f"Made {membership.effective_name} {'' if membership.admin else 'not '}an admin"
        )
        logger().info(
            f"{_describe_session(session)}, User {user_id}. Performing {action}, "
            f"new status is {membership.admin}.")
        membership.save()

    elif action == SessionAdminUserAction.CREATE_PATCHER_FILE:
        player_names = {i: f"Player {i + 1}" for i in range(session.num_rows)}

        for member in GameSessionMembership.non_observer_members(session):
            player_names[member.row] = member.effective_name

        layout_description = session.layout_description
        players_config = PlayersConfiguration(
            player_index=membership.row,
            player_names=player_names,
        )
        preset = layout_description.get_preset(players_config.player_index)
        cosmetic_patches = preset.game.data.layout.cosmetic_patches.from_json(
            arg)

        _add_audit_entry(sio, session,
                         f"Made an ISO for row {membership.row + 1}")

        data_factory = preset.game.patch_data_factory(layout_description,
                                                      players_config,
                                                      cosmetic_patches)
        try:
            return data_factory.create_data()
        except Exception as e:
            logger().exception("Error when creating patch data")
            raise InvalidAction(f"Unable to export game: {e}")

    elif action == SessionAdminUserAction.ABANDON:
        # FIXME
        raise InvalidAction("Abandon is NYI")

    _emit_session_meta_update(session)
Exemplo n.º 16
0
def game_session_admin_player(sio: ServerApp, session_id: int, user_id: int,
                              action: str, arg):
    _verify_has_admin(sio, session_id, user_id)
    action: SessionAdminUserAction = SessionAdminUserAction(action)

    session: GameSession = database.GameSession.get_by_id(session_id)
    membership = GameSessionMembership.get_by_ids(user_id, session_id)

    if action == SessionAdminUserAction.KICK:
        membership.delete_instance()
        if not list(session.players):
            session.delete_instance(recursive=True)
            logger().info(
                f"Session {session_id}. Kicking user {user_id} and deleting session."
            )
        else:
            logger().info(f"Session {session_id}. Kicking user {user_id}.")

    elif action == SessionAdminUserAction.MOVE:
        offset: int = arg
        if membership.is_observer is None:
            raise InvalidAction("Player is an observer")

        new_row = membership.row + offset
        if new_row < 0:
            raise InvalidAction("New position is negative")
        if new_row >= session.num_rows:
            raise InvalidAction("New position is beyond num of rows")

        team_members = [None] * session.num_rows
        for member in GameSessionMembership.non_observer_members(session):
            team_members[member.row] = member

        while (0 <= new_row <
               session.num_rows) and team_members[new_row] is not None:
            new_row += offset

        if new_row < 0 or new_row >= session.num_rows:
            raise InvalidAction("No empty slots found in this direction")

        with database.db.atomic():
            logger().info(
                f"Session {session_id}, User {user_id}. "
                f"Performing {action}, new row is {new_row}, from {membership.row}."
            )
            membership.row = new_row
            membership.save()

    elif action == SessionAdminUserAction.SWITCH_IS_OBSERVER:
        if membership.is_observer:
            membership.row = _find_empty_row(session)
        else:
            membership.row = None
        logger().info(
            f"Session {session_id}, User {user_id}. Performing {action}, new row is {membership.row}."
        )
        membership.save()

    elif action == SessionAdminUserAction.SWITCH_ADMIN:
        # Must be admin for this
        _verify_has_admin(sio, session_id, None, allow_when_no_admins=True)
        num_admins = GameSessionMembership.select().where(
            GameSessionMembership.session == session_id,
            GameSessionMembership.admin == True).count()

        if membership.admin and num_admins <= 1:
            raise InvalidAction("can't demote the only admin")

        membership.admin = not membership.admin
        logger().info(
            f"Session {session_id}, User {user_id}. Performing {action}, new status is {membership.admin}."
        )
        membership.save()

    elif action == SessionAdminUserAction.CREATE_PATCHER_FILE:
        cosmetic_patches = CosmeticPatches.from_json_dict(arg)
        player_names = {i: f"Player {i + 1}" for i in range(session.num_rows)}

        for member in GameSessionMembership.non_observer_members(session):
            player_names[member.row] = member.effective_name

        players_config = PlayersConfiguration(
            player_index=membership.row,
            player_names=player_names,
        )
        return patcher_file.create_patcher_file(session.layout_description,
                                                players_config,
                                                cosmetic_patches)

    elif action == SessionAdminUserAction.ABANDON:
        # FIXME
        raise InvalidAction("Abandon is NYI")

    _emit_session_update(session)
Exemplo n.º 17
0
def _players_configuration() -> PlayersConfiguration:
    return PlayersConfiguration(
        player_index=0,
        player_names={0: "Player 1"},
    )
Exemplo n.º 18
0
def test_create_hints_all_placed(hide_area: bool, multiworld: bool,
                                 empty_patches, default_echoes_configuration,
                                 default_prime_configuration):
    # Setup
    echoes_game = default_database.game_description_for(
        RandovaniaGame.METROID_PRIME_ECHOES)
    players_config = PlayersConfiguration(0, {
        0: "you",
        1: "them"
    } if multiworld else {0: "you"})

    patches = empty_patches.assign_new_pickups([
        (PickupIndex(17 + key),
         PickupTarget(
             pickup_creator.create_sky_temple_key(
                 key, echoes_game.resource_database), 0))
        for key in range(5 if multiworld else 9)
    ])
    patches = dataclasses.replace(patches,
                                  configuration=default_echoes_configuration)

    other_patches = empty_patches.assign_new_pickups([
        (PickupIndex(17 + key),
         PickupTarget(
             pickup_creator.create_sky_temple_key(
                 key, echoes_game.resource_database), 0))
        for key in range(5, 9)
    ])
    other_patches = dataclasses.replace(
        other_patches, configuration=default_prime_configuration)
    assets = [
        0xD97685FE, 0x32413EFD, 0xDD8355C3, 0x3F5F4EBA, 0xD09D2584, 0x3BAA9E87,
        0xD468F5B9, 0x2563AE34, 0xCAA1C50A
    ]

    locations = [
        "Sky Temple Grounds - Profane Path",
        "Sky Temple Grounds - Phazon Grounds",
        "Sky Temple Grounds - Ing Reliquary",
        "Great Temple - Transport A Access",
        "Great Temple - Temple Sanctuary",
        "Great Temple - Transport B Access",
        "Great Temple - Main Energy Controller",
        "Great Temple - Main Energy Controller",
        "Agon Wastes - Mining Plaza",
    ]
    other_locations = [
        "Chozo Ruins - Watery Hall Access",
        "Chozo Ruins - Watery Hall",
        "Chozo Ruins - Watery Hall",
        "Chozo Ruins - Dynamo",
    ]
    if multiworld:
        locations[-len(other_locations):] = other_locations

    expected = [{
        "asset_id":
        asset_id,
        "strings":
        _create_hint_text(hide_area, multiworld, i + 1, text)
    } for i, (asset_id, text) in enumerate(zip(assets, locations))]

    namer = EchoesHintNamer({0: patches, 1: other_patches}, players_config)

    # Run
    result = randovania.games.prime2.exporter.hints.create_stk_hints(
        {
            0: patches,
            1: other_patches
        } if multiworld else {0: patches}, players_config,
        echoes_game.resource_database, namer, hide_area)

    # Assert
    assert result == expected
Exemplo n.º 19
0
def test_create_hints_all_placed(hide_area: bool, empty_patches,
                                 echoes_game_description):
    # Setup
    patches = empty_patches.assign_new_pickups([
        (PickupIndex(17 + key),
         PickupTarget(
             pickup_creator.create_sky_temple_key(
                 key, echoes_game_description.resource_database), 0))
        for key in range(9)
    ])
    expected = [
        {
            "asset_id":
            0xD97685FE,
            "strings":
            _create_hint_text(hide_area, 1,
                              "Sky Temple Grounds - Profane Path")
        },
        {
            "asset_id":
            0x32413EFD,
            "strings":
            _create_hint_text(hide_area, 2,
                              "Sky Temple Grounds - Phazon Grounds")
        },
        {
            "asset_id":
            0xDD8355C3,
            "strings":
            _create_hint_text(hide_area, 3,
                              "Sky Temple Grounds - Ing Reliquary")
        },
        {
            "asset_id":
            0x3F5F4EBA,
            "strings":
            _create_hint_text(hide_area, 4,
                              "Great Temple - Transport A Access")
        },
        {
            "asset_id":
            0xD09D2584,
            "strings":
            _create_hint_text(hide_area, 5, "Great Temple - Temple Sanctuary")
        },
        {
            "asset_id":
            0x3BAA9E87,
            "strings":
            _create_hint_text(hide_area, 6,
                              "Great Temple - Transport B Access")
        },
        {
            "asset_id":
            0xD468F5B9,
            "strings":
            _create_hint_text(hide_area, 7,
                              "Great Temple - Main Energy Controller")
        },
        {
            "asset_id":
            0x2563AE34,
            "strings":
            _create_hint_text(hide_area, 8,
                              "Great Temple - Main Energy Controller")
        },
        {
            "asset_id":
            0xCAA1C50A,
            "strings":
            _create_hint_text(hide_area, 9, "Agon Wastes - Mining Plaza")
        },
    ]

    # Run
    result = sky_temple_key_hint.create_hints(
        {0: patches}, PlayersConfiguration(0, {0: "you"}),
        echoes_game_description.world_list, hide_area)

    # Assert
    assert result == expected
Exemplo n.º 20
0
 def players_configuration(self) -> PlayersConfiguration:
     return PlayersConfiguration(
         player_index=self.current_player_index,
         player_names=self._player_names,
     )
Exemplo n.º 21
0
def test_apply_layout(
    mock_ensure_no_menu_mod: MagicMock,
    mock_create_pak_backups: MagicMock,
    mock_add_menu_mod_to_files: MagicMock,
    mock_modern_api: MagicMock,
    mock_apply_patches: MagicMock,
    mock_create_progress_update_from_successive_messages: MagicMock,
    mock_save_to_file: MagicMock,
    include_menu_mod: bool,
    has_backup_path: bool,
):
    # Setup
    cosmetic_patches = MagicMock()
    description = LayoutDescription(
        version=randovania.VERSION,
        permalink=Permalink(
            seed_number=1,
            spoiler=False,
            presets={
                0:
                Preset(name="Name",
                       description="Desc",
                       base_preset_name=None,
                       patcher_configuration=PatcherConfiguration(
                           menu_mod=include_menu_mod,
                           warp_to_start=MagicMock(),
                       ),
                       layout_configuration=MagicMock())
            },
        ),
        all_patches={0: MagicMock()},
        item_order=(),
    )

    game_root = MagicMock(spec=Path())
    backup_files_path = MagicMock() if has_backup_path else None
    progress_update = MagicMock()
    player_config = PlayersConfiguration(0, {0: "you"})
    status_update = mock_create_progress_update_from_successive_messages.return_value

    # Run
    claris_randomizer.apply_layout(description, player_config,
                                   cosmetic_patches, backup_files_path,
                                   progress_update, game_root)

    # Assert
    mock_create_progress_update_from_successive_messages.assert_called_once_with(
        progress_update, 400 if include_menu_mod else 100)
    mock_ensure_no_menu_mod.assert_called_once_with(game_root,
                                                    backup_files_path,
                                                    status_update)
    if has_backup_path:
        mock_create_pak_backups.assert_called_once_with(
            game_root, backup_files_path, status_update)
    else:
        mock_create_pak_backups.assert_not_called()
    game_root.joinpath.assert_called_once_with(
        "files", "randovania.{}".format(LayoutDescription.file_extension()))
    mock_save_to_file.assert_called_once_with(description,
                                              game_root.joinpath.return_value)

    mock_modern_api.assert_called_once_with(game_root, status_update,
                                            description, player_config,
                                            cosmetic_patches)
    mock_apply_patches.assert_called_once_with(game_root,
                                               description.all_patches[0],
                                               cosmetic_patches)

    if include_menu_mod:
        mock_add_menu_mod_to_files.assert_called_once_with(
            game_root, status_update)
    else:
        mock_add_menu_mod_to_files.assert_not_called()
Exemplo n.º 22
0
def test_create_string_patches(
        stk_mode: SkyTempleKeyHintMode,
        mocker,
):
    # Setup
    game = MagicMock()
    all_patches = MagicMock()
    rng = MagicMock()
    player_config = PlayersConfiguration(0, {0: "you"})

    mock_item_create_hints: MagicMock = mocker.patch(
        "randovania.games.prime2.exporter.hints.create_patches_hints",
        autospec=True, return_value=["item", "hints"],
    )
    mock_stk_create_hints: MagicMock = mocker.patch(
        "randovania.games.prime2.exporter.hints.create_stk_hints",
        autospec=True, return_value=["show", "hints"],
    )
    mock_stk_hide_hints: MagicMock = mocker.patch(
        "randovania.games.prime2.exporter.hints.hide_stk_hints",
        autospec=True, return_value=["hide", "hints"],
    )
    mock_logbook_title_string_patches: MagicMock = mocker.patch(
        "randovania.games.prime2.exporter.patch_data_factory._logbook_title_string_patches",
        autospec=True, return_values=[],
    )

    mock_akul_testament: MagicMock = mocker.patch(
        "randovania.games.prime2.exporter.patch_data_factory._akul_testament_string_patch",
        autospec=True,
    )
    mock_akul_testament.return_values = []
    namer = MagicMock()

    # Run
    result = patch_data_factory._create_string_patches(
        HintConfiguration(sky_temple_keys=stk_mode),
        game,
        all_patches,
        namer,
        player_config,
        rng,
    )

    # Assert
    expected_result = ["item", "hints"]
    mock_item_create_hints.assert_called_once_with(all_patches, player_config, game.world_list, namer, rng)
    mock_logbook_title_string_patches.assert_called_once_with()
    mock_akul_testament.assert_called_once_with(namer)

    if stk_mode == SkyTempleKeyHintMode.DISABLED:
        mock_stk_hide_hints.assert_called_once_with(namer)
        mock_stk_create_hints.assert_not_called()
        expected_result.extend(["hide", "hints"])

    else:
        mock_stk_create_hints.assert_called_once_with(all_patches, player_config, game.resource_database,
                                                      namer, stk_mode == SkyTempleKeyHintMode.HIDE_AREA)
        mock_stk_hide_hints.assert_not_called()
        expected_result.extend(["show", "hints"])

    assert result == expected_result
Exemplo n.º 23
0
def _echoes_hint_exporter(echoes_game_patches) -> HintExporter:
    namer = EchoesHintNamer({0: echoes_game_patches},
                            PlayersConfiguration(0, {0: "You"}))
    return HintExporter(namer, random.Random(0), ["A Joke"])
Exemplo n.º 24
0
    async def _export_iso(self):
        layout = self.layout_description
        has_spoiler = layout.permalink.spoiler
        options = self._options

        if not options.is_alert_displayed(InfoAlert.FAQ):
            await async_dialog.message_box(
                self, QtWidgets.QMessageBox.Icon.Information, "FAQ",
                "Have you read the Randovania FAQ?\n"
                "It can be found in the main Randovania window → Help → FAQ")
            options.mark_alert_as_displayed(InfoAlert.FAQ)

        game = layout.permalink.get_preset(
            self.current_player_index).configuration.game

        if game == RandovaniaGame.PRIME3:
            return await self._show_dialog_for_prime3_layout()

        patcher = self._window_manager.patcher_provider.patcher_for_game(game)
        if patcher.is_busy:
            return await async_dialog.message_box(
                self, QtWidgets.QMessageBox.Critical, "Can't save ISO",
                "Error: Unable to save multiple ISOs at the same time,"
                "another window is saving an ISO right now.")

        dialog = GameInputDialog(options, patcher, layout.shareable_word_hash,
                                 has_spoiler, game)
        result = await async_dialog.execute_dialog(dialog)
        if result != QDialog.Accepted:
            return

        input_file = dialog.input_file
        output_file = dialog.output_file
        auto_save_spoiler = dialog.auto_save_spoiler
        players_config = PlayersConfiguration(
            player_index=self.current_player_index,
            player_names=self._player_names,
        )

        with options:
            options.output_directory = output_file.parent
            options.auto_save_spoiler = auto_save_spoiler

        patch_data = patcher.create_patch_data(layout, players_config,
                                               options.cosmetic_patches)

        def work(progress_update: ProgressUpdateCallable):
            patcher.patch_game(input_file,
                               output_file,
                               patch_data,
                               options.game_files_path,
                               progress_update=progress_update)

            if has_spoiler and auto_save_spoiler:
                layout.save_to_file(
                    output_file.with_suffix(
                        f".{LayoutDescription.file_extension()}"))

            progress_update(f"Finished!", 1)

        try:
            await self.run_in_background_async(work, "Exporting...")
        except Exception as e:
            logging.exception("Unable to export ISO")

            self.progress_update_signal.emit(f"Unable to export ISO: {e}",
                                             None)
            box = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Critical,
                                        "Unable to export ISO", str(e),
                                        QtWidgets.QMessageBox.Ok)
            common_qt_lib.set_default_window_icon(box)
            if e.__traceback__ is not None:
                box.setDetailedText("".join(
                    traceback.format_tb(e.__traceback__)))
            await async_dialog.execute_dialog(box)
Exemplo n.º 25
0
def test_create_hints_all_starting(hide_area: bool, multiworld: bool,
                                   empty_patches, echoes_game_description,
                                   default_echoes_configuration):
    # Setup
    players_config = PlayersConfiguration(0, {
        0: "you",
        1: "them"
    } if multiworld else {0: "you"})
    patches = empty_patches.assign_extra_initial_items({
        echoes_game_description.resource_database.get_item(
            echoes_items.SKY_TEMPLE_KEY_ITEMS[key]): 1
        for key in range(9)
    })
    patches = dataclasses.replace(patches,
                                  configuration=default_echoes_configuration)
    namer = EchoesHintNamer({0: patches}, players_config)

    expected = [
        {
            "asset_id": 0xD97685FE,
            "strings": make_starting_stk_hint(1)
        },
        {
            "asset_id": 0x32413EFD,
            "strings": make_starting_stk_hint(2)
        },
        {
            "asset_id": 0xDD8355C3,
            "strings": make_starting_stk_hint(3)
        },
        {
            "asset_id": 0x3F5F4EBA,
            "strings": make_starting_stk_hint(4)
        },
        {
            "asset_id": 0xD09D2584,
            "strings": make_starting_stk_hint(5)
        },
        {
            "asset_id": 0x3BAA9E87,
            "strings": make_starting_stk_hint(6)
        },
        {
            "asset_id": 0xD468F5B9,
            "strings": make_starting_stk_hint(7)
        },
        {
            "asset_id": 0x2563AE34,
            "strings": make_starting_stk_hint(8)
        },
        {
            "asset_id": 0xCAA1C50A,
            "strings": make_starting_stk_hint(9)
        },
    ]

    # Run
    result = randovania.games.prime2.exporter.hints.create_stk_hints(
        {0: patches}, players_config,
        echoes_game_description.resource_database, namer, hide_area)

    # Assert
    assert result == expected