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", }
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', }
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, )
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] }]
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
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
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)
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", }
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, ))
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, )
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
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"}))
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)
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)
def _players_configuration() -> PlayersConfiguration: return PlayersConfiguration( player_index=0, player_names={0: "Player 1"}, )
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
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
def players_configuration(self) -> PlayersConfiguration: return PlayersConfiguration( player_index=self.current_player_index, player_names=self._player_names, )
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()
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
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"])
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)
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