diff --git a/assets/i18n/de.json b/assets/i18n/de.json index a95f4d858..4336c7698 100644 --- a/assets/i18n/de.json +++ b/assets/i18n/de.json @@ -1281,7 +1281,6 @@ "loading_update_map_infos": "Aktualisieren der Kartenliste", "loading_update_text_infos": "Updating the list of texts files", "loading_check_maps_modified": "Checking maps", - "loading_rmxp_to_studio_maps_sync": "Synchronizing maps with RPG Maker XP", "creating_project_opening_path": "Auswahl des übergeordneten Ordners", "creating_project_checking": "Prüfen, ob das Projekt nicht existiert", "creating_project_child_folder_exist_error": "Das Projekt existiert bereits", @@ -1768,6 +1767,10 @@ "item_category_description_stone": "Evolution stones are special items that can trigger the evolution of certain Pokémon species when used on them.", "item_category_description_tech": "TMs are used to teach Pokémon new moves.", "end_of_rmxp_support": "Support for RPG Maker XP as a map creation tool will end in an upcoming update, and the use of Tiled will become mandatory.", + "rmxp_migration_dialog_title": "Migrate your project to Tiled", + "rmxp_migration_dialog_text": "This project uses RPG Maker XP as its map creation tool. Loading it will automatically migrate it to use Tiled. Please consult the migration guide to learn how to set up Tiled before continuing.", + "rmxp_migration_dialog_danger": "Make sure to back up your project before starting this process.", + "rmxp_migration_dialog_learn_more": "Migration guide", "edit_map_links": "Edit map links", "map_links_to_map": "Link map", "instructions": "Instructions", @@ -1901,6 +1904,18 @@ "add_base_stat_max_value_to_settings": "Adding the max base stat value to project settings", "full_map_dialog_message": "You can update all the maps in your project.\nThis process may take several minutes, depending on the number of maps in your project.", "full_map_dialog_success": "All maps have been successfully updated.", + "map_in_studio": "Map in Pokémon Studio", + "assign": "Assign", + "assigning": "Assigning", + "assign_tiled_maps": "Assign Tiled maps", + "assign_select_folder": "Select the folder containing the maps to assign", + "assign_select_maps": "Select the files to assign", + "assign_error": "Some maps cannot be assigned.", + "assign_selected_map_singular": "Assign the selected map", + "assign_selected_map_plural": "Assign selected {{amount}} maps", + "create_or_update_maps": "Creating or updating maps", + "assigning_tiled_maps_success": "Tiled maps successfully assigned", + "assigning_tiled_maps_error": "Error while assigning Tiled maps", "max_trainer_party_size": "Max trainer party size", "add_trainer_party_max_size_to_settings": "Add trainer party max size to settings" } diff --git a/assets/i18n/en.json b/assets/i18n/en.json index 8fdbc3e19..1506bcd21 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -1281,7 +1281,6 @@ "loading_update_map_infos": "Updating the list of maps", "loading_update_text_infos": "Updating the list of texts files", "loading_check_maps_modified": "Checking maps", - "loading_rmxp_to_studio_maps_sync": "Synchronizing maps with RPG Maker XP", "creating_project_opening_path": "Parent folder choice", "creating_project_checking": "Checking that the project does not exist", "creating_project_child_folder_exist_error": "The project already exists.", @@ -1768,6 +1767,10 @@ "item_category_description_stone": "Evolution stones are special items that can trigger the evolution of certain Pokémon species when used on them.", "item_category_description_tech": "TMs are used to teach Pokémon new moves.", "end_of_rmxp_support": "Support for RPG Maker XP as a map creation tool will end in an upcoming update, and the use of Tiled will become mandatory.", + "rmxp_migration_dialog_title": "Migrate your project to Tiled", + "rmxp_migration_dialog_text": "Your project uses RPG Maker XP for map creation. Only Tiled will be supported in version 3.0. To continue, you need to migrate your maps to Tiled, then enable Tiled mode. A guide is available to help you through these steps.", + "rmxp_migration_dialog_danger": "Make sure to back up your project before starting this process.", + "rmxp_migration_dialog_learn_more": "View migration guide", "edit_map_links": "Edit map links", "map_links_to_map": "Link map", "instructions": "Instructions", @@ -1901,6 +1904,18 @@ "add_base_stat_max_value_to_settings": "Adding the max base stat value to project settings", "full_map_dialog_message": "You can update all the maps in your project.\nThis process may take several minutes, depending on the number of maps in your project.", "full_map_dialog_success": "All maps have been successfully updated.", + "map_in_studio": "Map in Pokémon Studio", + "assign": "Assign", + "assigning": "Assigning", + "assign_tiled_maps": "Assign Tiled maps", + "assign_select_folder": "Select the folder containing the maps to assign", + "assign_select_maps": "Select the files to assign", + "assign_error": "Some maps cannot be assigned.", + "assign_selected_map_singular": "Assign the selected map", + "assign_selected_map_plural": "Assign selected {{amount}} maps", + "create_or_update_maps": "Creating or updating maps", + "assigning_tiled_maps_success": "Tiled maps successfully assigned", + "assigning_tiled_maps_error": "Error while assigning Tiled maps", "max_trainer_party_size": "Max trainer party size", "add_trainer_party_max_size_to_settings": "Add trainer party max size to settings" } diff --git a/assets/i18n/es.json b/assets/i18n/es.json index acbca43df..877573ae5 100644 --- a/assets/i18n/es.json +++ b/assets/i18n/es.json @@ -1281,7 +1281,6 @@ "loading_update_map_infos": "Actualizando la lista de mapas", "loading_update_text_infos": "Updating the list of texts files", "loading_check_maps_modified": "Checking maps", - "loading_rmxp_to_studio_maps_sync": "Synchronizing maps with RPG Maker XP", "creating_project_opening_path": "Elegir carpeta principal", "creating_project_checking": "Comprobando que el proyecto no existe", "creating_project_child_folder_exist_error": "El proyecto ya existe.", @@ -1768,6 +1767,10 @@ "item_category_description_stone": "Evolution stones are special items that can trigger the evolution of certain Pokémon species when used on them.", "item_category_description_tech": "TMs are used to teach Pokémon new moves.", "end_of_rmxp_support": "Support for RPG Maker XP as a map creation tool will end in an upcoming update, and the use of Tiled will become mandatory.", + "rmxp_migration_dialog_title": "Migrate your project to Tiled", + "rmxp_migration_dialog_text": "This project uses RPG Maker XP as its map creation tool. Loading it will automatically migrate it to use Tiled. Please consult the migration guide to learn how to set up Tiled before continuing.", + "rmxp_migration_dialog_danger": "Make sure to back up your project before starting this process.", + "rmxp_migration_dialog_learn_more": "Migration guide", "edit_map_links": "Edit map links", "map_links_to_map": "Link map", "instructions": "Instructions", @@ -1901,6 +1904,18 @@ "add_base_stat_max_value_to_settings": "Adding the max base stat value to project settings", "full_map_dialog_message": "You can update all the maps in your project.\nThis process may take several minutes, depending on the number of maps in your project.", "full_map_dialog_success": "All maps have been successfully updated.", + "map_in_studio": "Map in Pokémon Studio", + "assign": "Assign", + "assigning": "Assigning", + "assign_tiled_maps": "Assign Tiled maps", + "assign_select_folder": "Select the folder containing the maps to assign", + "assign_select_maps": "Select the files to assign", + "assign_error": "Some maps cannot be assigned.", + "assign_selected_map_singular": "Assign the selected map", + "assign_selected_map_plural": "Assign selected {{amount}} maps", + "create_or_update_maps": "Creating or updating maps", + "assigning_tiled_maps_success": "Tiled maps successfully assigned", + "assigning_tiled_maps_error": "Error while assigning Tiled maps", "max_trainer_party_size": "Max trainer party size", "add_trainer_party_max_size_to_settings": "Add trainer party max size to settings" } diff --git a/assets/i18n/fr.json b/assets/i18n/fr.json index e95bc000d..1ad497e6d 100644 --- a/assets/i18n/fr.json +++ b/assets/i18n/fr.json @@ -1281,7 +1281,6 @@ "loading_update_map_infos": "Mise à jour de la liste des cartes", "loading_update_text_infos": "Mise à jour de la liste des fichiers de textes", "loading_check_maps_modified": "Vérification des cartes", - "loading_rmxp_to_studio_maps_sync": "Synchronisation des cartes avec RPG Maker XP", "creating_project_opening_path": "Choix du dossier parent", "creating_project_checking": "Vérification que le projet n'existe pas", "creating_project_child_folder_exist_error": "Le projet existe déjà.", @@ -1768,6 +1767,10 @@ "item_category_description_stone": "Les pierres évolutives sont utilisées pour faire évoluer certains Pokémon.", "item_category_description_tech": "Les CT et CS sont des objets qui enseignent de nouvelles capacités aux Pokémon.", "end_of_rmxp_support": "Le support de RPG Maker XP en tant qu'outil de création de vos cartes sera arrêté dans une prochaine mise à jour et l'utilisation de Tiled sera obligatoire.", + "rmxp_migration_dialog_title": "Tiled est désormais obligatoire", + "rmxp_migration_dialog_text": "Votre projet utilise RPG Maker XP pour la création des cartes. Seul Tiled sera pris en charge dans la version 3.0 pour cette utilisation. Pour continuer, vous devez migrer vos cartes vers Tiled, puis activer ce mode. Un guide est disponible pour vous accompagner.", + "rmxp_migration_dialog_danger": "Pensez à faire une sauvegarde de votre projet avant de démarrer ces actions.", + "rmxp_migration_dialog_learn_more": "Consulter le guide", "edit_map_links": "Éditer les cartes liées", "map_links_to_map": "Liaisons de la carte", "instructions": "Instructions", @@ -1901,6 +1904,18 @@ "add_base_stat_max_value_to_settings": "Ajout du paramètre de statistique de base maximum aux paramètres du projet", "full_map_dialog_message": "Vous pouvez mettre à jour toutes les cartes de votre projet.\nCette opération peut prendre plusieurs minutes selon le nombre de cartes de votre projet.", "full_map_dialog_success": "Toutes les cartes ont été mises à jour avec succès.", + "map_in_studio": "Carte dans Pokémon Studio", + "assign": "Assigner", + "assigning": "Attribution", + "assign_tiled_maps": "Assigner des cartes Tiled", + "assign_select_folder": "Sélectionnez le dossier contenant les cartes à assigner", + "assign_select_maps": "Sélectionnez les fichiers à assigner", + "assign_error": "Certaines cartes ne peuvent pas être assignées.", + "assign_selected_map_singular": "Assigner la carte sélectionnée", + "assign_selected_map_plural": "Assigner les {{amount}} cartes", + "create_or_update_maps": "Création ou mise à jour des cartes", + "assigning_tiled_maps_success": "Cartes Tiled assignées avec succès", + "assigning_tiled_maps_error": "Erreur lors de l'assignation des cartes Tiled", "max_trainer_party_size": "Nombre maximum de créatures dans l'équipe d'un dresseur", "add_trainer_party_max_size_to_settings": "Ajout de la taille maximale de l'équipe d'un dresseur aux paramètres du projet" } diff --git a/assets/i18n/it.json b/assets/i18n/it.json index 9398c1a74..674fae787 100644 --- a/assets/i18n/it.json +++ b/assets/i18n/it.json @@ -1281,7 +1281,6 @@ "loading_update_map_infos": "Aggiornamento dell'elenco delle mappe", "loading_update_text_infos": "Aggiornamento dell'elenco dei file di testo", "loading_check_maps_modified": "Controllo delle mappe", - "loading_rmxp_to_studio_maps_sync": "Synchronizing maps with RPG Maker XP", "creating_project_opening_path": "Scelta della cartella principale", "creating_project_checking": "Verifica che il progetto non esista", "creating_project_child_folder_exist_error": "Il progetto esiste già.", @@ -1768,6 +1767,10 @@ "item_category_description_stone": "Evolution stones are special items that can trigger the evolution of certain Pokémon species when used on them.", "item_category_description_tech": "TMs are used to teach Pokémon new moves.", "end_of_rmxp_support": "Support for RPG Maker XP as a map creation tool will end in an upcoming update, and the use of Tiled will become mandatory.", + "rmxp_migration_dialog_title": "Migrate your project to Tiled", + "rmxp_migration_dialog_text": "This project uses RPG Maker XP as its map creation tool. Loading it will automatically migrate it to use Tiled. Please consult the migration guide to learn how to set up Tiled before continuing.", + "rmxp_migration_dialog_danger": "Make sure to back up your project before starting this process.", + "rmxp_migration_dialog_learn_more": "Migration guide", "edit_map_links": "Edit map links", "map_links_to_map": "Link map", "instructions": "Instructions", @@ -1901,6 +1904,18 @@ "add_base_stat_max_value_to_settings": "Adding the max base stat value to project settings", "full_map_dialog_message": "You can update all the maps in your project.\nThis process may take several minutes, depending on the number of maps in your project.", "full_map_dialog_success": "All maps have been successfully updated.", + "map_in_studio": "Map in Pokémon Studio", + "assign": "Assign", + "assigning": "Assigning", + "assign_tiled_maps": "Assign Tiled maps", + "assign_select_folder": "Select the folder containing the maps to assign", + "assign_select_maps": "Select the files to assign", + "assign_error": "Some maps cannot be assigned.", + "assign_selected_map_singular": "Assign the selected map", + "assign_selected_map_plural": "Assign selected {{amount}} maps", + "create_or_update_maps": "Creating or updating maps", + "assigning_tiled_maps_success": "Tiled maps successfully assigned", + "assigning_tiled_maps_error": "Error while assigning Tiled maps", "max_trainer_party_size": "Max trainer party size", "add_trainer_party_max_size_to_settings": "Add trainer party max size to settings" } diff --git a/assets/i18n/ja.json b/assets/i18n/ja.json index 57e79d0d9..e9acd7390 100644 --- a/assets/i18n/ja.json +++ b/assets/i18n/ja.json @@ -1281,7 +1281,6 @@ "loading_update_map_infos": "Updating the list of maps", "loading_update_text_infos": "Updating the list of texts files", "loading_check_maps_modified": "Checking maps", - "loading_rmxp_to_studio_maps_sync": "Synchronizing maps with RPG Maker XP", "creating_project_opening_path": "Parent folder choice", "creating_project_checking": "Checking that the project does not exist", "creating_project_child_folder_exist_error": "The project already exists.", @@ -1768,6 +1767,10 @@ "item_category_description_stone": "Evolution stones are special items that can trigger the evolution of certain Pokémon species when used on them.", "item_category_description_tech": "TMs are used to teach Pokémon new moves.", "end_of_rmxp_support": "Support for RPG Maker XP as a map creation tool will end in an upcoming update, and the use of Tiled will become mandatory.", + "rmxp_migration_dialog_title": "Migrate your project to Tiled", + "rmxp_migration_dialog_text": "This project uses RPG Maker XP as its map creation tool. Loading it will automatically migrate it to use Tiled. Please consult the migration guide to learn how to set up Tiled before continuing.", + "rmxp_migration_dialog_danger": "Make sure to back up your project before starting this process.", + "rmxp_migration_dialog_learn_more": "Migration guide", "edit_map_links": "Edit map links", "map_links_to_map": "Link map", "instructions": "Instructions", @@ -1901,6 +1904,18 @@ "add_base_stat_max_value_to_settings": "Adding the max base stat value to project settings", "full_map_dialog_message": "You can update all the maps in your project.\nThis process may take several minutes, depending on the number of maps in your project.", "full_map_dialog_success": "All maps have been successfully updated.", + "map_in_studio": "Map in Pokémon Studio", + "assign": "Assign", + "assigning": "Assigning", + "assign_tiled_maps": "Assign Tiled maps", + "assign_select_folder": "Select the folder containing the maps to assign", + "assign_select_maps": "Select the files to assign", + "assign_error": "Some maps cannot be assigned.", + "assign_selected_map_singular": "Assign the selected map", + "assign_selected_map_plural": "Assign selected {{amount}} maps", + "create_or_update_maps": "Creating or updating maps", + "assigning_tiled_maps_success": "Tiled maps successfully assigned", + "assigning_tiled_maps_error": "Error while assigning Tiled maps", "max_trainer_party_size": "Max trainer party size", "add_trainer_party_max_size_to_settings": "Add trainer party max size to settings" } diff --git a/assets/i18n/nl.json b/assets/i18n/nl.json index 5644e2d6c..4f12b37e5 100644 --- a/assets/i18n/nl.json +++ b/assets/i18n/nl.json @@ -1281,7 +1281,6 @@ "loading_update_map_infos": "Updating the list of maps", "loading_update_text_infos": "Updating the list of texts files", "loading_check_maps_modified": "Checking maps", - "loading_rmxp_to_studio_maps_sync": "Synchronizing maps with RPG Maker XP", "creating_project_opening_path": "Parent folder choice", "creating_project_checking": "Checking that the project does not exist", "creating_project_child_folder_exist_error": "The project already exists.", @@ -1768,6 +1767,10 @@ "item_category_description_stone": "Evolution stones are special items that can trigger the evolution of certain Pokémon species when used on them.", "item_category_description_tech": "TMs are used to teach Pokémon new moves.", "end_of_rmxp_support": "Support for RPG Maker XP as a map creation tool will end in an upcoming update, and the use of Tiled will become mandatory.", + "rmxp_migration_dialog_title": "Migrate your project to Tiled", + "rmxp_migration_dialog_text": "This project uses RPG Maker XP as its map creation tool. Loading it will automatically migrate it to use Tiled. Please consult the migration guide to learn how to set up Tiled before continuing.", + "rmxp_migration_dialog_danger": "Make sure to back up your project before starting this process.", + "rmxp_migration_dialog_learn_more": "Migration guide", "edit_map_links": "Edit map links", "map_links_to_map": "Link map", "instructions": "Instructions", @@ -1901,6 +1904,18 @@ "add_base_stat_max_value_to_settings": "Adding the max base stat value to project settings", "full_map_dialog_message": "You can update all the maps in your project.\nThis process may take several minutes, depending on the number of maps in your project.", "full_map_dialog_success": "All maps have been successfully updated.", + "map_in_studio": "Map in Pokémon Studio", + "assign": "Assign", + "assigning": "Assigning", + "assign_tiled_maps": "Assign Tiled maps", + "assign_select_folder": "Select the folder containing the maps to assign", + "assign_select_maps": "Select the files to assign", + "assign_error": "Some maps cannot be assigned.", + "assign_selected_map_singular": "Assign the selected map", + "assign_selected_map_plural": "Assign selected {{amount}} maps", + "create_or_update_maps": "Creating or updating maps", + "assigning_tiled_maps_success": "Tiled maps successfully assigned", + "assigning_tiled_maps_error": "Error while assigning Tiled maps", "max_trainer_party_size": "Max trainer party size", "add_trainer_party_max_size_to_settings": "Add trainer party max size to settings" } diff --git a/assets/i18n/pt.json b/assets/i18n/pt.json index 37bdb3411..94bd9e332 100644 --- a/assets/i18n/pt.json +++ b/assets/i18n/pt.json @@ -1281,7 +1281,6 @@ "loading_update_map_infos": "Update da lista de mapas", "loading_update_text_infos": "Atualização da lista de arquivos de texto", "loading_check_maps_modified": "Verificando mapas", - "loading_rmxp_to_studio_maps_sync": "Sincronizando mapas com RPG Maker XP", "creating_project_opening_path": "Escolha da pasta principal", "creating_project_checking": "Verificação de que o projeto já não existe", "creating_project_child_folder_exist_error": "O projeto já existe.", @@ -1768,6 +1767,10 @@ "item_category_description_stone": "Pedras de evolução são itens especiais que podem desencadear a evolução de certas espécies de Pokémon quando usadas neles.", "item_category_description_tech": "TMs são usados para ensinar novos ataques aos Pokémon.", "end_of_rmxp_support": "O suporte para o RPG Maker XP como uma ferramenta de criação de mapas terminará em uma atualização futura, e o uso do Tiled se tornará obrigatório.", + "rmxp_migration_dialog_title": "Migrate your project to Tiled", + "rmxp_migration_dialog_text": "Your project uses RPG Maker XP for map creation. Only Tiled will be supported in version 3.0. To continue, you need to migrate your maps to Tiled, then enable Tiled mode. A guide is available to help you through these steps.", + "rmxp_migration_dialog_danger": "Make sure to back up your project before starting this process.", + "rmxp_migration_dialog_learn_more": "View migration guide", "edit_map_links": "Editar links do mapa", "map_links_to_map": "Linkar um mapa", "instructions": "Instruções", @@ -1901,6 +1904,18 @@ "add_base_stat_max_value_to_settings": "Adding the max base stat value to project settings", "full_map_dialog_message": "You can update all the maps in your project.\nThis process may take several minutes, depending on the number of maps in your project.", "full_map_dialog_success": "All maps have been successfully updated.", + "map_in_studio": "Map in Pokémon Studio", + "assign": "Assign", + "assigning": "Assigning", + "assign_tiled_maps": "Assign Tiled maps", + "assign_select_folder": "Select the folder containing the maps to assign", + "assign_select_maps": "Select the files to assign", + "assign_error": "Some maps cannot be assigned.", + "assign_selected_map_singular": "Assign the selected map", + "assign_selected_map_plural": "Assign selected {{amount}} maps", + "create_or_update_maps": "Creating or updating maps", + "assigning_tiled_maps_success": "Tiled maps successfully assigned", + "assigning_tiled_maps_error": "Error while assigning Tiled maps", "max_trainer_party_size": "Max trainer party size", "add_trainer_party_max_size_to_settings": "Add trainer party max size to settings" } diff --git a/assets/i18n/zh_Hans.json b/assets/i18n/zh_Hans.json index bf36db224..ccab1c4ff 100644 --- a/assets/i18n/zh_Hans.json +++ b/assets/i18n/zh_Hans.json @@ -1281,7 +1281,6 @@ "loading_update_map_infos": "Updating the list of maps", "loading_update_text_infos": "Updating the list of texts files", "loading_check_maps_modified": "Checking maps", - "loading_rmxp_to_studio_maps_sync": "Synchronizing maps with RPG Maker XP", "creating_project_opening_path": "Parent folder choice", "creating_project_checking": "Checking that the project does not exist", "creating_project_child_folder_exist_error": "The project already exists.", @@ -1768,6 +1767,10 @@ "item_category_description_stone": "Evolution stones are special items that can trigger the evolution of certain Pokémon species when used on them.", "item_category_description_tech": "TMs are used to teach Pokémon new moves.", "end_of_rmxp_support": "Support for RPG Maker XP as a map creation tool will end in an upcoming update, and the use of Tiled will become mandatory.", + "rmxp_migration_dialog_title": "Migrate your project to Tiled", + "rmxp_migration_dialog_text": "This project uses RPG Maker XP as its map creation tool. Loading it will automatically migrate it to use Tiled. Please consult the migration guide to learn how to set up Tiled before continuing.", + "rmxp_migration_dialog_danger": "Make sure to back up your project before starting this process.", + "rmxp_migration_dialog_learn_more": "Migration guide", "edit_map_links": "Edit map links", "map_links_to_map": "Link map", "instructions": "Instructions", @@ -1901,6 +1904,18 @@ "add_base_stat_max_value_to_settings": "Adding the max base stat value to project settings", "full_map_dialog_message": "You can update all the maps in your project.\nThis process may take several minutes, depending on the number of maps in your project.", "full_map_dialog_success": "All maps have been successfully updated.", + "map_in_studio": "Map in Pokémon Studio", + "assign": "Assign", + "assigning": "Assigning", + "assign_tiled_maps": "Assign Tiled maps", + "assign_select_folder": "Select the folder containing the maps to assign", + "assign_select_maps": "Select the files to assign", + "assign_error": "Some maps cannot be assigned.", + "assign_selected_map_singular": "Assign the selected map", + "assign_selected_map_plural": "Assign selected {{amount}} maps", + "create_or_update_maps": "Creating or updating maps", + "assigning_tiled_maps_success": "Tiled maps successfully assigned", + "assigning_tiled_maps_error": "Error while assigning Tiled maps", "max_trainer_party_size": "Max trainer party size", "add_trainer_party_max_size_to_settings": "Add trainer party max size to settings" } diff --git a/assets/i18n/zh_Hant.json b/assets/i18n/zh_Hant.json index 48e85f25a..680b953d6 100644 --- a/assets/i18n/zh_Hant.json +++ b/assets/i18n/zh_Hant.json @@ -1281,7 +1281,6 @@ "loading_update_map_infos": "Updating the list of maps", "loading_update_text_infos": "Updating the list of texts files", "loading_check_maps_modified": "Checking maps", - "loading_rmxp_to_studio_maps_sync": "Synchronizing maps with RPG Maker XP", "creating_project_opening_path": "Parent folder choice", "creating_project_checking": "Checking that the project does not exist", "creating_project_child_folder_exist_error": "The project already exists.", @@ -1768,6 +1767,10 @@ "item_category_description_stone": "Evolution stones are special items that can trigger the evolution of certain Pokémon species when used on them.", "item_category_description_tech": "TMs are used to teach Pokémon new moves.", "end_of_rmxp_support": "Support for RPG Maker XP as a map creation tool will end in an upcoming update, and the use of Tiled will become mandatory.", + "rmxp_migration_dialog_title": "Migrate your project to Tiled", + "rmxp_migration_dialog_text": "This project uses RPG Maker XP as its map creation tool. Loading it will automatically migrate it to use Tiled. Please consult the migration guide to learn how to set up Tiled before continuing.", + "rmxp_migration_dialog_danger": "Make sure to back up your project before starting this process.", + "rmxp_migration_dialog_learn_more": "Migration guide", "edit_map_links": "Edit map links", "map_links_to_map": "Link map", "instructions": "Instructions", @@ -1901,6 +1904,18 @@ "add_base_stat_max_value_to_settings": "Adding the max base stat value to project settings", "full_map_dialog_message": "You can update all the maps in your project.\nThis process may take several minutes, depending on the number of maps in your project.", "full_map_dialog_success": "All maps have been successfully updated.", + "map_in_studio": "Map in Pokémon Studio", + "assign": "Assign", + "assigning": "Assigning", + "assign_tiled_maps": "Assign Tiled maps", + "assign_select_folder": "Select the folder containing the maps to assign", + "assign_select_maps": "Select the files to assign", + "assign_error": "Some maps cannot be assigned.", + "assign_selected_map_singular": "Assign the selected map", + "assign_selected_map_plural": "Assign selected {{amount}} maps", + "create_or_update_maps": "Creating or updating maps", + "assigning_tiled_maps_success": "Tiled maps successfully assigned", + "assigning_tiled_maps_error": "Error while assigning Tiled maps", "max_trainer_party_size": "Max trainer party size", "add_trainer_party_max_size_to_settings": "Add trainer party max size to settings" } diff --git a/forge.config.ts b/forge.config.ts index 7b2ad0a38..fc200dda4 100644 --- a/forge.config.ts +++ b/forge.config.ts @@ -1,10 +1,10 @@ -import type { ForgeConfig } from '@electron-forge/shared-types'; -import { MakerZIP } from '@electron-forge/maker-zip'; import { MakerDeb } from '@electron-forge/maker-deb'; import { MakerRpm } from '@electron-forge/maker-rpm'; -import { MakerNSIS } from './src/MakerNSIS'; -import { MakerAppImage } from '@reforged/maker-appimage'; +import { MakerZIP } from '@electron-forge/maker-zip'; import { VitePlugin } from '@electron-forge/plugin-vite'; +import type { ForgeConfig } from '@electron-forge/shared-types'; +import { MakerAppImage } from '@reforged/maker-appimage'; +import { MakerNSIS } from './src/MakerNSIS'; const config: ForgeConfig = { packagerConfig: { diff --git a/psdk-binaries/pokemonsdk b/psdk-binaries/pokemonsdk index 406792df1..53427b020 160000 --- a/psdk-binaries/pokemonsdk +++ b/psdk-binaries/pokemonsdk @@ -1 +1 @@ -Subproject commit 406792df1e02d0ab75a06b76a7086c9577278e1b +Subproject commit 53427b020ac340efa77301273362565b9d6b5c7c diff --git a/src/App.tsx b/src/App.tsx index cc5950313..3cf37bfb6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -22,7 +22,6 @@ import './i18n'; import DesignSystemRouterComponent from '@ds/DesignSystem.router'; import PocRouterComponent from '@poc/Poc.router'; import { TooltipContext } from '@ds/Tooltip/TooltipContext'; -import { EndSupportRMXPMapsBanner } from './views/components/EndSupportRMXPMapsBanner'; const App = () => { return ( @@ -34,7 +33,6 @@ const App = () => { - } /> } /> diff --git a/src/backendTasks/RMXP2StudioMapsSync.ts b/src/backendTasks/RMXP2StudioMapsSync.ts deleted file mode 100644 index 77b0f936d..000000000 --- a/src/backendTasks/RMXP2StudioMapsSync.ts +++ /dev/null @@ -1,192 +0,0 @@ -import log from 'electron-log'; -import path from 'path'; -import fs from 'fs'; -import fsPromise from 'fs/promises'; -import { defineBackendServiceFunction } from './defineBackendServiceFunction'; -import { readProjectFolder } from './readProjectData'; -import { MAP_DESCRIPTION_TEXT_ID, MAP_NAME_TEXT_ID, MAP_VALIDATOR, StudioMap } from '@modelEntities/map'; -import { padStr } from '@utils/PadStr'; -import { DEFAULT_MAP_INFO, StudioMapInfo } from '@modelEntities/mapInfo'; -import { createMapInfo } from '@utils/entityCreation'; -import { addLineCSV, loadCSV } from '@utils/textManagement'; -import { stringify } from 'csv-stringify/sync'; -import { addNewMapInfo } from '@utils/MapInfoUtils'; -import { readRMXPMapInfo } from './readRMXPMapInfo'; -import { RMXPMap, readRMXPMap } from './readRMXPMap'; -import { DbSymbol } from '@modelEntities/dbSymbol'; -import { parseJSON } from '@utils/json/parse'; - -export type RMXP2StudioMapsSyncInput = { projectPath: string }; - -const updatedMapNeeded = (projectPath: string, id: number) => { - const studioMapPath = path.join(projectPath, 'Data/Studio/Maps', `map${padStr(id, 3)}.json`); - const rmxpMapPath = path.join(projectPath, 'Data', `Map${padStr(id, 3)}.rxdata`); - return fs.statSync(rmxpMapPath).mtime > fs.statSync(studioMapPath).mtime; -}; - -const updatedCSVNeeded = (projectPath: string, mapInfoRMXPFilePath: string) => { - const csvPath = path.join(projectPath, 'Data/Text/Studio', `${MAP_NAME_TEXT_ID}.csv`); - return fs.statSync(mapInfoRMXPFilePath).mtime > fs.statSync(csvPath).mtime; -}; - -const getAudio = (rmxpMapData?: RMXPMap) => { - const defaultAudio = { name: '', volume: 100, pitch: 100 }; - if (!rmxpMapData) return { bgm: undefined, bgs: undefined }; - - const bgm = rmxpMapData.autoplayBgm ? rmxpMapData.bgm : defaultAudio; - const bgs = rmxpMapData.autoplayBgs ? rmxpMapData.bgs : defaultAudio; - return { bgm, bgs }; -}; - -const updateStudioMapInfo = async (rmxpMapInfoData: { id: number; name: string; parentId: number }[], projectPath: string) => { - const mapInfoStudioFilePath = path.join(projectPath, 'Data', 'Studio', 'map_info.json'); - let studioMapInfoData = DEFAULT_MAP_INFO; - - // Build the studio map info (all parentId = 0) - rmxpMapInfoData.map((rmxpMapInfo) => { - const newMapInfo = createMapInfo(studioMapInfoData, { - klass: 'MapInfoMap', - mapDbSymbol: `map${padStr(rmxpMapInfo.id, 3)}` as DbSymbol, - parentId: 0, - }); - studioMapInfoData = addNewMapInfo(studioMapInfoData, newMapInfo); - }); - - // Assign the parentId - const studioMapInfoDataValues = Object.values(studioMapInfoData); - studioMapInfoDataValues.forEach((mapInfo) => { - if (mapInfo.data.klass !== 'MapInfoMap') return; - - const mapDbSymbol = mapInfo.data.mapDbSymbol; - const rmxpMapInfo = rmxpMapInfoData.find((rmxpMapInfo) => mapDbSymbol === `map${padStr(rmxpMapInfo.id, 3)}`); - if (!rmxpMapInfo) return; - - const studioMapInfo = studioMapInfoDataValues.find( - (studioMapInfo) => studioMapInfo.data.klass === 'MapInfoMap' && studioMapInfo.data.mapDbSymbol === `map${padStr(rmxpMapInfo.parentId, 3)}` - ); - if (!studioMapInfo) return; - - mapInfo.data.parentId = studioMapInfo.id; - studioMapInfo.children.push(mapInfo.id); - studioMapInfo.hasChildren = true; - - // Clear the root - const index = studioMapInfoDataValues[0].children.findIndex((child) => child === mapInfo.id); - if (index === -1) return; - - studioMapInfoDataValues[0].children.splice(index, 1); - }); - - // Convert studioMapInfoDataValues array to record - studioMapInfoData = studioMapInfoDataValues.reduce((prev, mapInfo) => { - prev[mapInfo.id.toString()] = mapInfo; - return prev; - }, {} as StudioMapInfo); - - // Expand all items belonging to the root - studioMapInfoData[0].children.forEach((id) => { - studioMapInfoData[id].isExpanded = studioMapInfoData[id].children.length > 0; - }); - - await fsPromise.writeFile(mapInfoStudioFilePath, JSON.stringify(studioMapInfoData, null, 2)); -}; - -const createNewMap = (payload: { rmxpMap: { id: number; name: string }; rmxpMapData: RMXPMap | undefined; projectPath: string }) => { - const { bgm, bgs } = getAudio(payload.rmxpMapData); - const newMap = { - klass: 'Map', - id: payload.rmxpMap.id, - dbSymbol: `map${padStr(payload.rmxpMap.id, 3)}`, - stepsAverage: payload.rmxpMapData?.encounterStep || 1, - bgm: bgm ?? { name: '', volume: 100, pitch: 100 }, - bgs: bgs ?? { name: '', volume: 100, pitch: 100 }, - mtime: 1, - sha1: '', - tiledFilename: '', - } as StudioMap; - fsPromise.writeFile(path.join(payload.projectPath, 'Data/Studio/maps', `${newMap.dbSymbol}.json`), JSON.stringify(newMap, null, 2)); -}; - -const readMaps = async (projectPath: string) => { - const maps = await readProjectFolder(projectPath, 'maps'); - return maps.reduce((data, map) => { - const mapParsed = MAP_VALIDATOR.safeParse(parseJSON(map.data, map.filename)); - if (mapParsed.success) { - data.push(mapParsed.data); - } - return data; - }, [] as StudioMap[]); -}; - -const updateMap = (payload: { - rmxpMap: { id: number; name: string }; - rmxpMapData: RMXPMap | undefined; - studioMap: StudioMap; - projectPath: string; -}) => { - if (!updatedMapNeeded(payload.projectPath, payload.rmxpMap.id)) return; - - const { bgm, bgs } = getAudio(payload.rmxpMapData); - const studioMap = payload.studioMap; - const updateMap = { - ...studioMap, - stepsAverage: payload.rmxpMapData?.encounterStep || studioMap.stepsAverage, - bgm: bgm === undefined ? studioMap.bgm : bgm, - bgs: bgs === undefined ? studioMap.bgs : bgs, - }; - fsPromise.writeFile(path.join(payload.projectPath, 'Data/Studio/maps', `${updateMap.dbSymbol}.json`), JSON.stringify(updateMap, null, 2)); -}; - -const deleteMaps = async (studioMaps: StudioMap[], rmxpMapIds: number[], projectPath: string) => { - return await studioMaps.reduce(async (lastPromise, studioMap) => { - await lastPromise; - - if (!rmxpMapIds.includes(studioMap.id)) { - const mapToDeletePath = path.join(projectPath, 'Data/Studio/maps', `${studioMap.dbSymbol}.json`); - if (fs.existsSync(mapToDeletePath)) { - fsPromise.unlink(path.join(projectPath, 'Data/Studio/maps', `${studioMap.dbSymbol}.json`)); - } - } - }, Promise.resolve()); -}; - -const RMXP2StudioMapsSync = async (payload: RMXP2StudioMapsSyncInput) => { - log.info('rmxp-to-studio-maps-sync', payload); - const mapInfoRMXPFilePath = path.join(payload.projectPath, 'Data', 'MapInfos.rxdata'); - const rmxpMapInfoData = await readRMXPMapInfo(mapInfoRMXPFilePath); - const rmxpMapIds = rmxpMapInfoData.map(({ id }) => id); - - const studioMaps = await readMaps(payload.projectPath); - const mapNames = await loadCSV(path.join(payload.projectPath, 'Data/Text/Studio', `${MAP_NAME_TEXT_ID}.csv`)); - const mapNameColumnLength = mapNames[0]?.length || 0; - const mapDescriptions = await loadCSV(path.join(payload.projectPath, 'Data/Text/Studio', `${MAP_DESCRIPTION_TEXT_ID}.csv`)); - const mapDescrColumnLength = mapDescriptions[0]?.length || 0; - - await updateStudioMapInfo(rmxpMapInfoData, payload.projectPath); - - await rmxpMapInfoData.reduce(async (lastPromise, rmxpMap) => { - await lastPromise; - - const rmxpMapData = await readRMXPMap(payload.projectPath, rmxpMap.id); - const studioMap = studioMaps.find((studioMap) => studioMap.id === rmxpMap.id); - if (studioMap) { - updateMap({ rmxpMap, rmxpMapData, studioMap, projectPath: payload.projectPath }); - if (updatedCSVNeeded(payload.projectPath, mapInfoRMXPFilePath)) { - addLineCSV(new Array(mapNameColumnLength).fill(rmxpMap.name), rmxpMap.id + 1, 0, mapNames); - } - } else { - createNewMap({ rmxpMap, rmxpMapData, projectPath: payload.projectPath }); - addLineCSV(new Array(mapNameColumnLength).fill(rmxpMap.name), rmxpMap.id + 1, 0, mapNames); - addLineCSV(new Array(mapDescrColumnLength).fill(''), rmxpMap.id + 1, 0, mapDescriptions); - } - }, Promise.resolve()); - - await deleteMaps(studioMaps, rmxpMapIds, payload.projectPath); - await fsPromise.writeFile(path.join(payload.projectPath, 'Data/Text/Studio', `${MAP_NAME_TEXT_ID}.csv`), stringify(mapNames)); - await fsPromise.writeFile(path.join(payload.projectPath, 'Data/Text/Studio', `${MAP_DESCRIPTION_TEXT_ID}.csv`), stringify(mapDescriptions)); - - log.info('rmxp-to-studio-maps-sync/success'); - return {}; -}; - -export const registerRMXP2StudioMapsSync = defineBackendServiceFunction('rmxp-to-studio-maps-sync', RMXP2StudioMapsSync); diff --git a/src/backendTasks/readProjectData.ts b/src/backendTasks/readProjectData.ts index 6a08b5718..818ea562f 100644 --- a/src/backendTasks/readProjectData.ts +++ b/src/backendTasks/readProjectData.ts @@ -8,9 +8,7 @@ import { StudioTextInfo } from '@modelEntities/textInfo'; import { defineBackendServiceFunction } from './defineBackendServiceFunction'; import { ChannelNames, sendProgress } from '@utils/BackendTask'; import { StudioMapInfo } from '@modelEntities/mapInfo'; -import { DEFAULT_EVENT_TREE } from '@modelEntities/event/event-tree'; import type { StudioEventTree } from '@modelEntities/event/event-tree'; -import { setLoadedMaps } from './studioMapToRMXPConversionFacilitator'; import { parseJSON } from '@utils/json/parse'; const projectDataKeys = [ @@ -94,8 +92,6 @@ const readProjectData = async (payload: ReadProjectDataInput, event: IpcMainEven Promise.resolve({ textInfos, mapInfo, eventTree } as ProjectDataFromBackEnd), ); - // Store the loaded maps so the converter will know which maps changed - setLoadedMaps(projectData.maps.map((m) => m.data)); log.info('read-project-data/success'); return projectData; }; diff --git a/src/backendTasks/readRMXPMapInfo.ts b/src/backendTasks/readRMXPMapInfo.ts index 2629c247d..8146b451e 100644 --- a/src/backendTasks/readRMXPMapInfo.ts +++ b/src/backendTasks/readRMXPMapInfo.ts @@ -1,7 +1,7 @@ import log from 'electron-log'; -import path from 'path'; import fs from 'fs'; import fsPromise from 'fs/promises'; +import path from 'path'; import { isMarshalHash, isMarshalStandardObject, Marshal } from 'ts-marshal'; import { defineBackendServiceFunction } from './defineBackendServiceFunction'; @@ -42,7 +42,7 @@ export const readRMXPMapInfo = async (mapInfoFilePath: string) => { const { __class, __extendedModules, __default, ...mapInfos } = marshalData; const mapInfoRecords = Object.entries(mapInfos) .map(([id, data]) => - isMapInfoObject(data) ? { id: Number(id), order: data['@order'], name: data['@name'], parentId: data['@parent_id'] } : undefined + isMapInfoObject(data) ? { id: Number(id), order: data['@order'], name: data['@name'], parentId: data['@parent_id'] } : undefined, ) .filter((data: T): data is Exclude => !!data); const rmxpMapData = mapInfoRecords.sort((a, b) => a.order - b.order).map(({ id, name, parentId }) => ({ id, name, parentId })); diff --git a/src/backendTasks/startCompilation.ts b/src/backendTasks/startCompilation.ts index b0fa4a31f..333d68130 100644 --- a/src/backendTasks/startCompilation.ts +++ b/src/backendTasks/startCompilation.ts @@ -1,18 +1,18 @@ -import { ChildProcessWithoutNullStreams, spawn } from 'child_process'; import { StudioCompilation } from '@components/compilation/CompilationDialogSchema'; -import { IpcMainEvent } from 'electron'; -import { ChannelNames, sendProgress } from '@utils/BackendTask'; -import { defineBackendServiceFunction } from './defineBackendServiceFunction'; -import { getPSDKBinariesPath } from '@services/getPSDKVersion'; import { INFO_CONFIG_VALIDATOR } from '@modelEntities/config'; -import { parseJSON } from '@utils/json/parse'; import { PROJECT_VALIDATOR } from '@modelEntities/project'; -import { RMXP2StudioSafetyNet } from '@services/startPSDK'; -import windowManager from './windowManager'; +import { getPSDKBinariesPath } from '@services/getPSDKVersion'; +import { ChannelNames, sendProgress } from '@utils/BackendTask'; +import { parseJSON } from '@utils/json/parse'; +import { ChildProcessWithoutNullStreams, spawn } from 'child_process'; +import { IpcMainEvent } from 'electron'; +import log from 'electron-log'; import { existsSync } from 'fs'; import fsPromise from 'fs/promises'; import path from 'path'; -import log from 'electron-log'; +import { ensureBootLoadFile } from '../services/startPSDK'; +import { defineBackendServiceFunction } from './defineBackendServiceFunction'; +import windowManager from './windowManager'; export type StartCompilationInput = { configuration: StudioCompilation; @@ -28,7 +28,7 @@ const BUFFER_LIMIT = 10; // The data is send to the front-end when the loggerBuf let progression = 0; const getSpawnArgs = (rubyPath: string, projectPath: string, ...args: string[]): [string, string[]] => { - RMXP2StudioSafetyNet(projectPath); + ensureBootLoadFile(projectPath); if (process.platform === 'win32') { const gamePath = path.join(projectPath, 'Game.rb'); return [path.join(rubyPath, 'rubyw.exe'), ['--disable=gems,rubyopt,did_you_mean', gamePath, ...args]]; diff --git a/src/hooks/useEvent/useEvents.ts b/src/hooks/useEvent/useEvents.ts index 829abc0bf..d24f47278 100644 --- a/src/hooks/useEvent/useEvents.ts +++ b/src/hooks/useEvent/useEvents.ts @@ -52,7 +52,6 @@ export const useMapInfo = () => { return { mapInfo, - isRMXPMode: !state.projectStudio.isTiledMode, setMapInfo, setPartialMapInfo, state, diff --git a/src/hooks/useMapCopy/useMapCopyProcessor.ts b/src/hooks/useMapCopy/useMapCopyProcessor.ts index 1e5c69ee9..07203b84f 100644 --- a/src/hooks/useMapCopy/useMapCopyProcessor.ts +++ b/src/hooks/useMapCopy/useMapCopyProcessor.ts @@ -31,7 +31,7 @@ export const useMapCopyProcessor = () => { } return mapImport( - { filesToImport: filesToImport, tiledFilesSrcPath: dirname(tmxFile), rmxpMapInfo: [], copyMode: true }, + { filesToImport: filesToImport, tiledFilesSrcPath: dirname(tmxFile), copyMode: true }, () => { binding.current.onSuccess({}); return setState(DEFAULT_PROCESS_STATE); diff --git a/src/hooks/useMapImport/helpers.ts b/src/hooks/useMapImport/helpers.ts index 776ee5a2a..8b7d7bc58 100644 --- a/src/hooks/useMapImport/helpers.ts +++ b/src/hooks/useMapImport/helpers.ts @@ -1,12 +1,11 @@ -import { MutableRefObject } from 'react'; +import { RefObject } from 'react'; import type { MapImportError, MapImportFunctionBinding } from './types'; -import log from 'electron-log'; -export const fail = (binding: MutableRefObject, mapImportError: MapImportError[], genericError?: string) => { - log.error( - 'Failed to import the maps', - mapImportError.filter((err) => err.errorMessage) +export const fail = (binding: RefObject, mapImportError: MapImportError[], genericError?: string) => { + window.api.log.error( + 'Failed to assign the maps', + mapImportError.filter((err) => err.errorMessage), ); - if (genericError) log.error('Failed to import the maps: Generic error: ', genericError); + if (genericError) window.api.log.error('Failed to assign the maps: Generic error: ', genericError); binding.current.onFailure(mapImportError, genericError); }; diff --git a/src/hooks/useMapImport/index.ts b/src/hooks/useMapImport/index.ts index 731fd6e95..4988cc58f 100644 --- a/src/hooks/useMapImport/index.ts +++ b/src/hooks/useMapImport/index.ts @@ -1,23 +1,22 @@ -import type { MapImportFailureCallback, MapImportSuccessCallback, RMXPMapInfo } from './types'; +import type { MapImportFiles } from '@components/world/map/editors/MapImport/MapImportType'; import { DEFAULT_PROCESS_STATE, useProcess } from '@hooks/useProcess'; +import type { MapImportFailureCallback, MapImportSuccessCallback } from './types'; import { useMapImportProcessor } from './useMapImportProcessor'; -import type { MapImportFiles } from '@components/world/map/editors/MapImport/MapImportType'; export const useMapImport = () => { const { processors, binding } = useMapImportProcessor(); const setState = useProcess(processors, DEFAULT_PROCESS_STATE); return ( - payload: { filesToImport: MapImportFiles[]; tiledFilesSrcPath: string; rmxpMapInfo: RMXPMapInfo[]; copyMode?: boolean }, + payload: { filesToImport: MapImportFiles[]; tiledFilesSrcPath: string; copyMode?: boolean }, onSuccess: MapImportSuccessCallback, - onFailure: MapImportFailureCallback + onFailure: MapImportFailureCallback, ) => { binding.current = { onFailure, onSuccess }; setState({ state: 'import', filesToImport: payload.filesToImport, tiledFilesSrcPath: payload.tiledFilesSrcPath, - rmxpMapInfo: payload.rmxpMapInfo, copyMode: payload.copyMode || false, }); }; diff --git a/src/hooks/useMapImport/types.ts b/src/hooks/useMapImport/types.ts index c2b77ef30..76c5f4458 100644 --- a/src/hooks/useMapImport/types.ts +++ b/src/hooks/useMapImport/types.ts @@ -1,25 +1,20 @@ import type { MapImportFiles } from '@components/world/map/editors/MapImport/MapImportType'; -import type { RMXPMap } from '@src/backendTasks/readRMXPMap'; import type { PartialStudioMap } from 'ts-tiled-converter'; -export type RMXPMapInfo = { id: number; name: string }; export type MapToImport = { mtime: number; sha1: string; tileMetadata?: PartialStudioMap['tileMetadata'] } & Omit< MapImportFiles, 'shouldBeImport' | 'error' | 'filename' >; -export type MapToImportWithRMXPMap = { rmxpMap?: RMXPMap } & MapToImport; export type MapImportError = { path: string; errorMessage?: string }; export type MapImportFailureCallback = (error: MapImportError[], genericError?: string) => void; export type MapImportSuccessCallback = (payload: Record) => void; export type MapImportStateObject = | { state: 'done' } - | { state: 'import'; filesToImport: MapImportFiles[]; tiledFilesSrcPath: string; rmxpMapInfo: RMXPMapInfo[]; copyMode: boolean } - | { state: 'copyTmxFiles'; mapsToImport: MapToImport[]; tiledFilesSrcPath: string; rmxpMapInfo: RMXPMapInfo[]; copyMode: boolean } - | { state: 'addMissingRMXPMaps'; mapsToImport: MapToImport[]; rmxpMapInfo: RMXPMapInfo[] } - | { state: 'getRMXPMapsData'; mapsToImport: MapToImport[]; rmxpMapIds: number[] } - | { state: 'generatingOverviews'; mapsToImport: MapToImport[]; rmxpMaps: Record; rmxpMapIds: number[] } - | { state: 'createNewMaps'; mapsToImport: MapToImport[]; rmxpMaps: Record; rmxpMapIds: number[] }; + | { state: 'import'; filesToImport: MapImportFiles[]; tiledFilesSrcPath: string; copyMode: boolean } + | { state: 'copyTmxFiles'; mapsToImport: MapToImport[]; tiledFilesSrcPath: string; copyMode: boolean } + | { state: 'generatingOverviews'; mapsToImport: MapToImport[] } + | { state: 'createOrUpdateMaps'; mapsToImport: MapToImport[] }; export type MapImportFunctionBinding = { onSuccess: MapImportSuccessCallback; onFailure: MapImportFailureCallback; diff --git a/src/hooks/useMapImport/useMapImportProcessor.ts b/src/hooks/useMapImport/useMapImportProcessor.ts index 601c26af4..2f00fd3eb 100644 --- a/src/hooks/useMapImport/useMapImportProcessor.ts +++ b/src/hooks/useMapImport/useMapImportProcessor.ts @@ -1,27 +1,25 @@ -import { useLoaderRef } from '@utils/loaderContext'; -import { useMemo, useRef } from 'react'; -import { useTranslation } from 'react-i18next'; -import type { MapImportFunctionBinding, MapImportStateObject, MapToImport } from './types'; -import { DEFAULT_PROCESS_STATE, PROCESS_DONE_STATE, SpecialStateProcessors } from '@hooks/useProcess'; import { MapImportFiles } from '@components/world/map/editors/MapImport/MapImportType'; -import type { PartialStudioMap } from 'ts-tiled-converter'; import { toAsyncProcess } from '@hooks/Helper'; -import { fail } from './helpers'; -import { useProjectMapLinks, useProjectMaps } from '@hooks/useProjectData'; import { useMapInfo } from '@hooks/useMapInfo'; -import { createMap, createMapInfo } from '@utils/entityCreation'; -import { StudioMapInfoMap } from '@modelEntities/mapInfo'; +import { DEFAULT_PROCESS_STATE, PROCESS_DONE_STATE, SpecialStateProcessors } from '@hooks/useProcess'; +import { useProjectMapLinks, useProjectMaps } from '@hooks/useProjectData'; import { MAP_DESCRIPTION_TEXT_ID, MAP_NAME_TEXT_ID } from '@modelEntities/map'; -import { useSetProjectText } from '@utils/ReadingProjectText'; -import { useGlobalState } from '@src/GlobalStateProvider'; +import { StudioMapInfoMap } from '@modelEntities/mapInfo'; import { Sha1 } from '@modelEntities/sha1'; +import { useGlobalState } from '@src/GlobalStateProvider'; +import { cloneEntity } from '@utils/cloneEntity'; +import { createMap, createMapInfo } from '@utils/entityCreation'; +import { parseJSON } from '@utils/json/parse'; +import { useLoaderRef } from '@utils/loaderContext'; import { addNewMapInfo } from '@utils/MapInfoUtils'; -import { padStr } from '@utils/PadStr'; -import { DbSymbol } from '@modelEntities/dbSymbol'; -import { RMXPMap } from '@src/backendTasks/readRMXPMap'; import { createMapLinkFromMainMapId } from '@utils/MapLinkUtils'; +import { useSetProjectText } from '@utils/ReadingProjectText'; import { getSetting } from '@utils/settings'; -import { parseJSON } from '@utils/json/parse'; +import { useMemo, useRef } from 'react'; +import { useTranslation } from 'react-i18next'; +import type { PartialStudioMap } from 'ts-tiled-converter'; +import { fail } from './helpers'; +import type { MapImportFunctionBinding, MapImportStateObject, MapToImport } from './types'; const DEFAULT_BINDING: MapImportFunctionBinding = { onFailure: () => {}, @@ -46,8 +44,8 @@ export const useMapImportProcessor = () => { const processors: SpecialStateProcessors = useMemo( () => ({ ...PROCESS_DONE_STATE, - import: ({ filesToImport, tiledFilesSrcPath, rmxpMapInfo, copyMode }, setState) => { - loaderRef.current.open('importing_tiled_maps', 1, 6, t('reading_data_tiled_files')); + import: ({ filesToImport, tiledFilesSrcPath, copyMode }, setState) => { + loaderRef.current.open('importing_tiled_maps', 1, 4, t('reading_data_tiled_files')); const tiledMetadata: PartialStudioMap[] = []; const importTmxFiles = (files: MapImportFiles[], tiledMetadata: PartialStudioMap[], index = 0) => { @@ -56,17 +54,17 @@ export const useMapImportProcessor = () => { setState(DEFAULT_PROCESS_STATE); fail( binding, - files.map((file) => ({ path: file.path, errorMessage: file.error })) + files.map((file) => ({ path: file.path, errorMessage: file.error })), ); } else { const mapsToImport = files.map((file, index) => ({ path: file.path, mapName: file.mapName, - mapId: file.mapId, + dbSymbol: file.dbSymbol, mtime: 1, ...tiledMetadata[index], })); - setState({ state: 'copyTmxFiles', mapsToImport, tiledFilesSrcPath, rmxpMapInfo, copyMode }); + setState({ state: 'copyTmxFiles', mapsToImport, tiledFilesSrcPath, copyMode }); } return () => {}; } @@ -81,14 +79,14 @@ export const useMapImportProcessor = () => { ({ errorMessage }) => { file.error = errorMessage; importTmxFiles(files, tiledMetadata, ++index); - } + }, ); }; return importTmxFiles(filesToImport, tiledMetadata); }, - copyTmxFiles: ({ mapsToImport, tiledFilesSrcPath, rmxpMapInfo, copyMode }, setState) => { - loaderRef.current.setProgress(2, 6, t('copy_tiled_files')); + copyTmxFiles: ({ mapsToImport, tiledFilesSrcPath, copyMode }, setState) => { + loaderRef.current.setProgress(2, 4, t('copy_tiled_files')); return window.api.copyTiledFiles( { projectPath: globalState.projectPath!, tiledMaps: JSON.stringify(mapsToImport), tiledSrcPath: tiledFilesSrcPath }, ({ tiledMaps, tiledMapsName }) => { @@ -97,62 +95,20 @@ export const useMapImportProcessor = () => { setState(DEFAULT_PROCESS_STATE); } else { const mapsToImport: MapToImport[] = parseJSON(tiledMaps, tiledMapsName); - setState({ state: 'addMissingRMXPMaps', mapsToImport, rmxpMapInfo }); + setState({ state: 'generatingOverviews', mapsToImport }); } }, ({ errorMessage }) => { setState(DEFAULT_PROCESS_STATE); fail(binding, mapsToImport, errorMessage); - } + }, ); }, - addMissingRMXPMaps: ({ mapsToImport, rmxpMapInfo }, setState) => { - loaderRef.current.setProgress(3, 6, t('add_missing_rmxp_maps')); - const studioMaps = Object.values(maps); - return toAsyncProcess(() => { - rmxpMapInfo.forEach(({ id: rmxpMapId, name }) => { - if (mapsToImport.find(({ mapId }) => mapId === rmxpMapId) || studioMaps.find(({ id }) => id === rmxpMapId)) return; - - mapsToImport.push({ mapName: name, mtime: 1, sha1: '', path: '', tileMetadata: undefined, mapId: rmxpMapId }); - }); - // the news maps must be create after the maps with a map id defined - mapsToImport.sort((a, b) => { - return (a.mapId || 999_999) - (b.mapId || 999_999); - }); - setState({ state: 'getRMXPMapsData', mapsToImport, rmxpMapIds: rmxpMapInfo.map(({ id }) => id) }); - }); - }, - getRMXPMapsData: ({ mapsToImport, rmxpMapIds }, setState) => { - loaderRef.current.setProgress(4, 6, t('read_data_rmxp_maps')); - const rmxpMaps: Record = {}; - - const readRMXPMap = (index = 0) => { - if (index >= rmxpMapIds.length) { - setState({ state: 'generatingOverviews', mapsToImport, rmxpMaps, rmxpMapIds }); - return () => {}; - } - - const mapId = rmxpMapIds[index]; - return window.api.readRMXPMap( - { projectPath: globalState.projectPath!, mapId }, - (payload) => { - rmxpMaps[mapId] = payload.rmxpMapData; - readRMXPMap(++index); - }, - ({ errorMessage }) => { - setState(DEFAULT_PROCESS_STATE); - fail(binding, mapsToImport, errorMessage); - } - ); - }; - - return readRMXPMap(); - }, - generatingOverviews: ({ mapsToImport, rmxpMaps, rmxpMapIds }, setState) => { - loaderRef.current.setProgress(5, 6, t('map_overviews_generating')); + generatingOverviews: ({ mapsToImport }, setState) => { + loaderRef.current.setProgress(3, 4, t('map_overviews_generating')); const generatingMapOverview = (index = 0): (() => void) => { if (index >= mapsToImport.length) { - setState({ state: 'createNewMaps', mapsToImport, rmxpMaps, rmxpMapIds }); + setState({ state: 'createOrUpdateMaps', mapsToImport }); return () => {}; } const tiledFilename = mapsToImport[index].path; @@ -163,7 +119,7 @@ export const useMapImportProcessor = () => { ({ errorMessage }) => { setState(DEFAULT_PROCESS_STATE); fail(binding, mapsToImport, errorMessage); - } + }, ); } else { return toAsyncProcess(() => generatingMapOverview(++index)); @@ -171,9 +127,9 @@ export const useMapImportProcessor = () => { }; return generatingMapOverview(); }, - createNewMaps: ({ mapsToImport, rmxpMaps, rmxpMapIds }, setState) => { + createOrUpdateMaps: ({ mapsToImport }, setState) => { return toAsyncProcess(() => { - loaderRef.current.setProgress(6, 6, t('create_new_maps')); + loaderRef.current.setProgress(4, 4, t('create_or_update_maps')); if (mapsToImport.length === 0) { // update the selected maplink by default const mapLinkValues = Object.values(mapLinks); @@ -185,42 +141,36 @@ export const useMapImportProcessor = () => { } const mapToImport = mapsToImport[0]; - const rmxpMap = mapToImport.mapId !== undefined ? rmxpMaps[mapToImport.mapId] : undefined; - const newMap = createMap( - maps, - 30, - mapToImport.path, - { name: '', volume: 100, pitch: 100 }, - { name: '', volume: 100, pitch: 100 }, - rmxpMapIds - ); - if (mapToImport.mapId !== undefined) { - newMap.id = mapToImport.mapId; - newMap.dbSymbol = `map${padStr(newMap.id, 3)}` as DbSymbol; - } - newMap.mtime = mapToImport.mtime; - newMap.sha1 = mapToImport.sha1 as Sha1; - newMap.tileMetadata = mapToImport.tileMetadata; - if (rmxpMap) { - newMap.bgm = rmxpMap.bgm; - newMap.bgs = rmxpMap.bgs; - newMap.stepsAverage = rmxpMap.encounterStep; + if (mapToImport.dbSymbol !== undefined) { + const updateMap = cloneEntity(maps[mapToImport.dbSymbol]); + updateMap.tiledFilename = mapToImport.path; + updateMap.mtime = mapToImport.mtime; + updateMap.sha1 = mapToImport.sha1 as Sha1; + updateMap.tileMetadata = mapToImport.tileMetadata; + const dbSymbol = updateMap.dbSymbol; + setText(MAP_NAME_TEXT_ID, updateMap.id, mapToImport.mapName); + setMap({ [dbSymbol]: updateMap }, { map: dbSymbol }); + } else { + const newMap = createMap(maps, 30, mapToImport.path, { name: '', volume: 100, pitch: 100 }, { name: '', volume: 100, pitch: 100 }); + newMap.mtime = mapToImport.mtime; + newMap.sha1 = mapToImport.sha1 as Sha1; + newMap.tileMetadata = mapToImport.tileMetadata; + const dbSymbol = newMap.dbSymbol; + const newMapInfoMap = createMapInfo(mapInfo, { klass: 'MapInfoMap', mapDbSymbol: dbSymbol, parentId: 0 }) as StudioMapInfoMap; + const newMapInfo = addNewMapInfo(mapInfo, newMapInfoMap); + const mapLink = createMapLinkFromMainMapId(mapLinks, newMap.id); + setMapLink({ [mapLink.dbSymbol]: mapLink }); + setText(MAP_NAME_TEXT_ID, newMap.id, mapToImport.mapName); + setText(MAP_DESCRIPTION_TEXT_ID, newMap.id, ''); + setMap({ [dbSymbol]: newMap }, { map: dbSymbol }); + setMapInfo(newMapInfo); } - const dbSymbol = newMap.dbSymbol; - const newMapInfoMap = createMapInfo(mapInfo, { klass: 'MapInfoMap', mapDbSymbol: dbSymbol, parentId: 0 }) as StudioMapInfoMap; - const newMapInfo = addNewMapInfo(mapInfo, newMapInfoMap); - const mapLink = createMapLinkFromMainMapId(mapLinks, newMap.id); - setMapLink({ [mapLink.dbSymbol]: mapLink }); - setText(MAP_NAME_TEXT_ID, newMap.id, mapToImport.mapName); - setText(MAP_DESCRIPTION_TEXT_ID, newMap.id, ''); - setMap({ [dbSymbol]: newMap }, { map: dbSymbol }); - setMapInfo(newMapInfo); mapsToImport.shift(); }); }, }), // eslint-disable-next-line react-hooks/exhaustive-deps - [maps] + [maps], ); return { processors, binding }; diff --git a/src/hooks/useMapInfo.ts b/src/hooks/useMapInfo.ts index 829abc0bf..d24f47278 100644 --- a/src/hooks/useMapInfo.ts +++ b/src/hooks/useMapInfo.ts @@ -52,7 +52,6 @@ export const useMapInfo = () => { return { mapInfo, - isRMXPMode: !state.projectStudio.isTiledMode, setMapInfo, setPartialMapInfo, state, diff --git a/src/hooks/usePage.ts b/src/hooks/usePage.ts index 567ec1e65..026d847f0 100644 --- a/src/hooks/usePage.ts +++ b/src/hooks/usePage.ts @@ -114,8 +114,7 @@ export const useMapPage = () => { map, hasMap: dbSymbol !== '__undef__', hasMapModified: state.mapsModified.length !== 0, - isRMXPMode: !state.projectStudio.isTiledMode, - disabledOpenTiled: !state.projectStudio.isTiledMode || !map?.tiledFilename, + disabledOpenTiled: !map?.tiledFilename, state, }; }; diff --git a/src/hooks/useProjectLoad/index.ts b/src/hooks/useProjectLoad/index.ts index 87430b9ac..4301d4542 100644 --- a/src/hooks/useProjectLoad/index.ts +++ b/src/hooks/useProjectLoad/index.ts @@ -1,4 +1,9 @@ -import type { ProjectLoadFailureCallback, ProjectLoadIntegrityFailureCallback, ProjectLoadSuccessCallback } from './types'; +import type { + ProjectLoadFailureCallback, + ProjectLoadIntegrityFailureCallback, + ProjectLoadRmxpMigrationCallback, + ProjectLoadSuccessCallback, +} from './types'; import { DEFAULT_PROCESS_STATE, useProcess } from '@hooks/useProcess'; import { useProjectLoadProcessor } from './useProjectLoadProcessor'; @@ -10,9 +15,10 @@ export const useProjectLoad = () => { payload: { projectDirName?: string }, onSuccess: ProjectLoadSuccessCallback, onFailure: ProjectLoadFailureCallback, - onIntegrityFailure: ProjectLoadIntegrityFailureCallback + onIntegrityFailure: ProjectLoadIntegrityFailureCallback, + onRmxpMigration: ProjectLoadRmxpMigrationCallback, ) => { - binding.current = { onFailure, onIntegrityFailure, onSuccess }; - setState(payload.projectDirName ? { state: 'readingVersion', projectDirName: payload.projectDirName } : { state: 'choosingProjectFile' }); + binding.current = { onFailure, onIntegrityFailure, onSuccess, onRmxpMigration }; + setState(payload.projectDirName ? { state: 'preCheckRmxpMode', projectDirName: payload.projectDirName } : { state: 'choosingProjectFile' }); }; }; diff --git a/src/hooks/useProjectLoad/types.ts b/src/hooks/useProjectLoad/types.ts index f524b3b3f..994790206 100644 --- a/src/hooks/useProjectLoad/types.ts +++ b/src/hooks/useProjectLoad/types.ts @@ -26,12 +26,13 @@ export type ProjectLoadIntegrityFailureCallback = (count: number) => void; export type ProjectLoadStateObject = | { state: 'done' } | { state: 'choosingProjectFile' } + | { state: 'preCheckRmxpMode'; projectDirName: string } + | { state: 'migrateToTiledMode'; projectDirName: string } | { state: 'readingVersion'; projectDirName: string } | { state: 'readProjectMetadata'; projectDirName: string; studioVersion: string } | { state: 'migrateProjectData'; projectDirName: string; studioVersion: string; projectVersion: string } | { state: 'writeProjectMetadata'; projectDirName: string; studioVersion: string; projectMetaData: StudioProject } | { state: 'updateTextInfos'; projectDirName: string; studioVersion: string; projectMetaData: StudioProject } - | { state: 'RMXP2StudioMapsSync'; projectDirName: string; studioVersion: string; projectMetaData: StudioProject } | { state: 'readProjectConfigs'; projectDirName: string; studioVersion: string; projectMetaData: StudioProject } | { state: 'readProjectData'; @@ -73,8 +74,10 @@ export type ProjectLoadStateObject = lastPSDKVersion: PSDKVersion; } | { state: 'openProject'; preState: PreGlobalState }; +export type ProjectLoadRmxpMigrationCallback = (onContinue: () => void) => void; export type ProjectLoadFunctionBinding = { onSuccess: ProjectLoadSuccessCallback; onFailure: ProjectLoadFailureCallback; onIntegrityFailure: ProjectLoadIntegrityFailureCallback; + onRmxpMigration: ProjectLoadRmxpMigrationCallback; }; diff --git a/src/hooks/useProjectLoad/useProjectLoadProcessor.ts b/src/hooks/useProjectLoad/useProjectLoadProcessor.ts index c136cef5b..c2d5a902b 100644 --- a/src/hooks/useProjectLoad/useProjectLoadProcessor.ts +++ b/src/hooks/useProjectLoad/useProjectLoadProcessor.ts @@ -6,7 +6,7 @@ import type { ProjectLoadFunctionBinding, ProjectLoadStateObject } from './types import { DEFAULT_PROCESS_STATE, PROCESS_DONE_STATE, SpecialStateProcessors } from '@hooks/useProcess'; import { toAsyncProcess } from '@hooks/Helper'; import { fail, handleFailure } from './helpers'; -import { PROJECT_VALIDATOR, PROJECT_VERSION_VALIDATOR } from '@modelEntities/project'; +import { PROJECT_VALIDATOR, PROJECT_VERSION_VALIDATOR, StudioProject } from '@modelEntities/project'; import { useDefaultTextInfoTranslation } from '@hooks/useDefaultTextInfoTranslation'; import i18n from '@src/i18n'; import { SavingMap, SavingConfigMap, SavingTextMap } from '@utils/SavingUtils'; @@ -21,6 +21,7 @@ const DEFAULT_BINDING: ProjectLoadFunctionBinding = { onFailure: () => {}, onIntegrityFailure: () => {}, onSuccess: () => {}, + onRmxpMigration: (_onContinue) => {}, }; const STEPS_TOTAL = 15; @@ -38,11 +39,43 @@ export const useProjectLoadProcessor = () => { loaderRef.current.open('loading_project', 0, 0, t('importing_project_choose_project')); return window.api.chooseProjectFileToOpen( { fileType: 'studio' }, - ({ dirName }) => setState({ state: 'readingVersion', projectDirName: dirName }), + ({ dirName }) => setState({ state: 'preCheckRmxpMode', projectDirName: dirName }), () => { setState(DEFAULT_PROCESS_STATE); loaderRef.current.close(); - } + }, + ); + }, + preCheckRmxpMode: (state, setState) => { + loaderRef.current.open('loading_project', 0, 0, t('loading_project_meta')); + return window.api.readProjectMetadata( + { path: state.projectDirName }, + ({ metaData }) => { + const isRmxpProject = (metaData as StudioProject & { isTiledMode: boolean }).isTiledMode === false; + if (isRmxpProject) { + loaderRef.current.close(); + binding.current.onRmxpMigration(() => setState({ state: 'migrateToTiledMode', projectDirName: state.projectDirName })); + } else { + setState({ state: 'readingVersion', projectDirName: state.projectDirName }); + } + }, + handleFailure(setState, binding), + ); + }, + migrateToTiledMode: (state, setState) => { + loaderRef.current.open('loading_project', 0, 0, t('loading_project_rmxp_migration')); + return window.api.readProjectMetadata( + { path: state.projectDirName }, + ({ metaData }) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { isTiledMode, ...metaDataWithoutTiledMode } = metaData as StudioProject & { isTiledMode: boolean }; + return window.api.writeProjectMetadata( + { path: state.projectDirName, metaData: JSON.stringify(metaDataWithoutTiledMode, null, 2) }, + () => setState({ state: 'readingVersion', projectDirName: state.projectDirName }), + handleFailure(setState, binding), + ); + }, + handleFailure(setState, binding), ); }, readingVersion: (state, setState) => { @@ -51,7 +84,7 @@ export const useProjectLoadProcessor = () => { return window.api.getStudioVersion( {}, (projectVersion) => setState({ ...state, state: 'readProjectMetadata', studioVersion: projectVersion.studioVersion }), - handleFailure(setState, binding) + handleFailure(setState, binding), ); }, readProjectMetadata: (state, setState) => { @@ -76,7 +109,7 @@ export const useProjectLoadProcessor = () => { setState({ ...state, state: 'migrateProjectData', projectVersion: projectVersion.data.studioVersion }); } }, - handleFailure(setState, binding) + handleFailure(setState, binding), ); }, migrateProjectData: (state, setState) => @@ -88,36 +121,24 @@ export const useProjectLoadProcessor = () => { handleFailure(setState, binding), (payload) => { loaderRef.current.open('migrating_data', payload.step, payload.total, payload.stepText); - } + }, ), writeProjectMetadata: (state, setState) => { loaderRef.current.open('loading_project', 4, STEPS_TOTAL, t('importing_project_writing_meta')); return window.api.writeProjectMetadata( { path: state.projectDirName, metaData: JSON.stringify(state.projectMetaData, null, 2) }, () => setState({ ...state, state: 'updateTextInfos' }), - handleFailure(setState, binding) + handleFailure(setState, binding), ); }, updateTextInfos: (state, setState) => { loaderRef.current.setProgress(5, STEPS_TOTAL, t('loading_update_text_infos')); return window.api.updateTextInfos( { projectPath: state.projectDirName, currentLanguage: i18n.language, textInfoTranslation: defaultTextInfoTranslation() }, - () => setState({ ...state, state: 'RMXP2StudioMapsSync' }), - handleFailure(setState, binding) + () => setState({ ...state, state: 'readProjectConfigs' }), + handleFailure(setState, binding), ); }, - RMXP2StudioMapsSync: (state, setState) => { - if (state.projectMetaData.isTiledMode === false) { - loaderRef.current.setProgress(6, STEPS_TOTAL, t('loading_rmxp_to_studio_maps_sync')); - return window.api.RMXP2StudioMapsSync( - { projectPath: state.projectDirName }, - () => setState({ ...state, state: 'readProjectConfigs' }), - handleFailure(setState, binding) - ); - } else { - return toAsyncProcess(() => setState({ ...state, state: 'readProjectConfigs' })); - } - }, readProjectConfigs: (state, setState) => { loaderRef.current.setProgress(7, STEPS_TOTAL, t('loading_project_config')); return window.api.readProjectConfigs( @@ -131,7 +152,7 @@ export const useProjectLoadProcessor = () => { fail(binding, error); } }, - handleFailure(setState, binding) + handleFailure(setState, binding), ); }, readProjectData: (state, setState) => { @@ -139,7 +160,7 @@ export const useProjectLoadProcessor = () => { return window.api.readProjectData( { path: state.projectDirName }, (projectData) => setState({ ...state, state: 'readProjectText', projectData }), - handleFailure(setState, binding) + handleFailure(setState, binding), ); }, readProjectText: (state, setState) => { @@ -147,7 +168,7 @@ export const useProjectLoadProcessor = () => { return window.api.readProjectTexts( { path: state.projectDirName }, (projectTexts) => setState({ ...state, state: 'deserializeProjectData', projectTexts }), - handleFailure(setState, binding) + handleFailure(setState, binding), ); }, deserializeProjectData: (state, setState) => { @@ -192,7 +213,7 @@ export const useProjectLoadProcessor = () => { tiledExecPath: getSetting('tiledPath'), }, ({ dbSymbols }) => setState({ ...state, state: 'readCurrentPSDKVersion', mapsModified: dbSymbols }), - handleFailure(setState, binding) + handleFailure(setState, binding), ); }, readCurrentPSDKVersion: (state, setState) => { @@ -227,7 +248,7 @@ export const useProjectLoadProcessor = () => { }, finalizeGlobalState: (state, setState) => { return toAsyncProcess(() => { - loaderRef.current.setProgress(15, STEPS_TOTAL, t('loading_project_identifier')); + loaderRef.current.setProgress(14, STEPS_TOTAL, t('loading_project_identifier')); sessionStorage.clear(); // Clear the whole session storage when loading is done so we don't carry garbage from other projects const selectedDataIdentifier = generateSelectedIdentifier(state.preState); const globalState = { @@ -266,7 +287,7 @@ export const useProjectLoadProcessor = () => { }); }, }), - [] + [], ); return { processors, binding }; diff --git a/src/hooks/useProjectNew/types.ts b/src/hooks/useProjectNew/types.ts index a1b883cb8..f7a379a22 100644 --- a/src/hooks/useProjectNew/types.ts +++ b/src/hooks/useProjectNew/types.ts @@ -3,7 +3,7 @@ import type { LatestNewProject } from '@src/backendTasks/checkDownloadNewProject export const DefaultLanguages = ['en', 'fr', 'it', 'de', 'es', 'ko', 'kana'] as const; export type DefaultLanguageType = (typeof DefaultLanguages)[number]; -export type NewProjectData = Omit & { +export type NewProjectData = Omit & { icon?: string; defaultLanguage: DefaultLanguageType; multiLanguage: boolean; diff --git a/src/hooks/useProjectNew/useProjectNewProcessor.ts b/src/hooks/useProjectNew/useProjectNewProcessor.ts index 2f1cffa20..0c0f42ee9 100644 --- a/src/hooks/useProjectNew/useProjectNewProcessor.ts +++ b/src/hooks/useProjectNew/useProjectNewProcessor.ts @@ -124,7 +124,6 @@ export const useProjectNewProcessor = () => { title: newProjectData.title, studioVersion: state.studioVersion, iconPath: 'project_icon.png', - isTiledMode: true, languagesTranslation: DEFAULT_OTHER_LANGUAGES, }, null, diff --git a/src/hooks/useProjectSave/useProjectSaveProcessor.ts b/src/hooks/useProjectSave/useProjectSaveProcessor.ts index c0c4674d8..33e682662 100644 --- a/src/hooks/useProjectSave/useProjectSaveProcessor.ts +++ b/src/hooks/useProjectSave/useProjectSaveProcessor.ts @@ -92,9 +92,8 @@ export const useProjectSaveProcessor = () => { }, saveRMXPMapInfo: (state, setState) => { loaderRef.current.setProgress(6, STEPS_TOTAL, t('saving_rmxp_map_info')); - if (!globalState.savingMapInfo || globalState.projectStudio.isTiledMode !== true) { - return toAsyncProcess(() => setState({ ...state, state: 'saveEventTree' })); - } + if (!globalState.savingMapInfo) return toAsyncProcess(() => setState({ ...state, state: 'saveEventTree' })); + return window.api.saveRMXPMapInfo( { projectPath: state.projectPath, @@ -145,7 +144,7 @@ export const useProjectSaveProcessor = () => { }); }, resetSaving: (_, setState) => { - loaderRef.current.setProgress(10, STEPS_TOTAL, t('saving_reset')); + loaderRef.current.setProgress(9, STEPS_TOTAL, t('saving_reset')); return toAsyncProcess(() => { setGlobalState({ ...globalState, diff --git a/src/hooks/useRMXP2StudioMapsUpdate/helpers.ts b/src/hooks/useRMXP2StudioMapsUpdate/helpers.ts deleted file mode 100644 index 962b538fc..000000000 --- a/src/hooks/useRMXP2StudioMapsUpdate/helpers.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Dispatch, MutableRefObject, SetStateAction } from 'react'; -import { RMXP2StudioMapsUpdateFunctionBinding, RMXP2StudioMapsUpdateStateObject } from './types'; -import { ProjectData } from '@src/GlobalStateProvider'; -import { DbSymbol } from '@modelEntities/dbSymbol'; - -export const fail = (binding: MutableRefObject, error: unknown) => { - window.api.log.error('Failed to synchronise maps:', error); - binding.current.onFailure({ errorMessage: `${error instanceof Error ? error.message : error}` }); -}; - -export const handleFailure = - (setState: Dispatch>, binding: MutableRefObject) => - ({ errorMessage }: { errorMessage: string | string[] }) => { - setState({ state: 'done' }); - fail(binding, errorMessage); - }; - -export const getSelectedMap = (newMaps: ProjectData['maps'], currentSelectedMap: DbSymbol) => { - if (newMaps[currentSelectedMap]) return currentSelectedMap; - - return Object.keys(newMaps)[0] || '__undef__'; -}; diff --git a/src/hooks/useRMXP2StudioMapsUpdate/index.ts b/src/hooks/useRMXP2StudioMapsUpdate/index.ts deleted file mode 100644 index 3a67b472e..000000000 --- a/src/hooks/useRMXP2StudioMapsUpdate/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { RMXP2StudioMapsUpdateFailureCallback, RMXP2StudioMapsUpdateSuccessCallback } from './types'; -import { DEFAULT_PROCESS_STATE, useProcess } from '@hooks/useProcess'; -import { useRMXP2StudioMapsUpdateProcessor } from './useRMXP2StudioMapsUpdateProcessor'; - -export const useRMXP2StudioMapsUpdate = () => { - const { processors, binding } = useRMXP2StudioMapsUpdateProcessor(); - const setState = useProcess(processors, DEFAULT_PROCESS_STATE); - - return (onSuccess: RMXP2StudioMapsUpdateSuccessCallback, onFailure: RMXP2StudioMapsUpdateFailureCallback) => { - binding.current = { onFailure, onSuccess }; - setState({ state: 'synchronise' }); - }; -}; diff --git a/src/hooks/useRMXP2StudioMapsUpdate/types.ts b/src/hooks/useRMXP2StudioMapsUpdate/types.ts deleted file mode 100644 index d65e1565e..000000000 --- a/src/hooks/useRMXP2StudioMapsUpdate/types.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { StudioMap } from '@modelEntities/map'; -import { StudioMapInfo } from '@modelEntities/mapInfo'; -import { ProjectText } from '@src/GlobalStateProvider'; - -export type RMXP2StudioMapsUpdateFailureCallback = (error: { errorMessage: string }) => void; -export type RMXP2StudioMapsUpdateSuccessCallback = (payload: Record) => void; -export type RMXP2StudioMapsUpdateStateObject = - | { state: 'done' } - | { state: 'synchronise' } - | { state: 'readMaps' } - | { state: 'updateMap'; maps: StudioMap[]; mapInfo: StudioMapInfo; mapNames: string[][]; mapDescriptions: string[][] }; -export type RMXP2StudioMapsUpdateFunctionBinding = { - onSuccess: RMXP2StudioMapsUpdateSuccessCallback; - onFailure: RMXP2StudioMapsUpdateFailureCallback; -}; diff --git a/src/hooks/useRMXP2StudioMapsUpdate/useRMXP2StudioMapsUpdateProcessor.ts b/src/hooks/useRMXP2StudioMapsUpdate/useRMXP2StudioMapsUpdateProcessor.ts deleted file mode 100644 index 1b1e5b998..000000000 --- a/src/hooks/useRMXP2StudioMapsUpdate/useRMXP2StudioMapsUpdateProcessor.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { useLoaderRef } from '@utils/loaderContext'; -import { useMemo, useRef } from 'react'; -import { useTranslation } from 'react-i18next'; -import type { RMXP2StudioMapsUpdateFunctionBinding, RMXP2StudioMapsUpdateStateObject } from './types'; -import { DEFAULT_PROCESS_STATE, PROCESS_DONE_STATE, SpecialStateProcessors } from '@hooks/useProcess'; -import { getSelectedMap, handleFailure } from './helpers'; -import { toAsyncProcess } from '@hooks/Helper'; -import { useGlobalState } from '@src/GlobalStateProvider'; -import { deserializeZodData, zodDataToEntries } from '@utils/SerializationUtils'; -import { MAP_DESCRIPTION_TEXT_ID, MAP_NAME_TEXT_ID, MAP_VALIDATOR } from '@modelEntities/map'; -import { MAP_INFO_VALIDATOR } from '@modelEntities/mapInfo'; -import { DbSymbol } from '@modelEntities/dbSymbol'; -import { parseJSON } from '@utils/json/parse'; - -const DEFAULT_BINDING: RMXP2StudioMapsUpdateFunctionBinding = { - onFailure: () => {}, - onSuccess: () => {}, -}; - -export const useRMXP2StudioMapsUpdateProcessor = () => { - const [globalState, setGlobalState] = useGlobalState(); - const loaderRef = useLoaderRef(); - const { t } = useTranslation(); - const binding = useRef(DEFAULT_BINDING); - - const processors: SpecialStateProcessors = useMemo( - () => ({ - ...PROCESS_DONE_STATE, - synchronise: (_, setState) => { - loaderRef.current.open('updating_maps', 1, 3, t('update_rmxp_maps')); - return window.api.RMXP2StudioMapsSync( - { projectPath: globalState.projectPath! }, - () => setState({ state: 'readMaps' }), - handleFailure(setState, binding) - ); - }, - readMaps: (_, setState) => { - loaderRef.current.setProgress(2, 3, t('read_data_rmxp_maps')); - return window.api.readMaps( - { projectPath: globalState.projectPath! }, - ({ maps, mapInfo, mapNames, mapDescriptions }) => { - const mapsResult = deserializeZodData(maps, MAP_VALIDATOR); - const mapInfoResult = MAP_INFO_VALIDATOR.safeParse(parseJSON(mapInfo, 'Data/Studio/map_info.json')); - if (mapsResult.integrityFailureCount.count === 0 && mapInfoResult.success) { - setState({ state: 'updateMap', maps: mapsResult.input, mapInfo: mapInfoResult.data, mapNames, mapDescriptions }); - } else { - const errorMessage: string[] = []; - if (!mapInfoResult.success) errorMessage.push('Failed to parse the map_info.json file', mapInfoResult.error.message); - if (mapsResult.integrityFailureCount.count !== 0) { - errorMessage.push(`Failed to load ${mapsResult.integrityFailureCount.count} map(s)`); - } - handleFailure(setState, binding)({ errorMessage }); - } - }, - handleFailure(setState, binding) - ); - }, - updateMap: ({ maps, mapInfo, mapNames, mapDescriptions }, setState) => { - return toAsyncProcess(() => { - loaderRef.current.setProgress(3, 3, t('update_maps')); - const projectDataMaps = zodDataToEntries(maps); - const selectedMap = getSelectedMap(projectDataMaps, globalState.selectedDataIdentifier.map as DbSymbol); - setGlobalState((state) => ({ - ...state, - projectData: { - ...state.projectData, - maps: projectDataMaps, - }, - projectText: { - ...state.projectText, - [MAP_NAME_TEXT_ID]: mapNames, - [MAP_DESCRIPTION_TEXT_ID]: mapDescriptions, - }, - selectedDataIdentifier: { - ...state.selectedDataIdentifier, - map: selectedMap, - }, - mapInfo, - })); - binding.current.onSuccess({}); - return setState(DEFAULT_PROCESS_STATE); - }); - }, - }), - // eslint-disable-next-line react-hooks/exhaustive-deps - [globalState] - ); - - return { processors, binding }; -}; diff --git a/src/main/index.ts b/src/main/index.ts index fa30f563a..29263dce9 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,64 +1,62 @@ /* eslint global-require: off, no-console: off */ +import { registerCheckDownloadNewProject } from '@src/backendTasks/checkDownloadNewProject'; +import { registerCheckMapsModified } from '@src/backendTasks/checkMapsModified'; +import { registerConvertRMXPEventsToStudioEvents } from '@src/backendTasks/convertRMXPEventsToStudioEvents'; +import { registerConvertTiledMapToTileMetadata } from '@src/backendTasks/convertTiledMapToTileMetadata'; +import { registerCopyTiledFiles } from '@src/backendTasks/copyTiledFiles'; +import { registerDownloadFile } from '@src/backendTasks/downloadFile'; +import { registerGeneratingMapOverview } from '@src/backendTasks/generatingMapOverview'; +import { registerGetCompilationConfig } from '@src/backendTasks/getCompilationConfig'; +import { registerGetFilePathsFromFolder } from '@src/backendTasks/getFilePathsFromFolder'; +import { registerOpenCompilationWindow } from '@src/backendTasks/openCompilationWindow'; +import { registerOpenStudioLogsFolder } from '@src/backendTasks/openStudioLogsFolder'; +import { registerOpenTiled } from '@src/backendTasks/openTiled'; +import { registerReadCsvFile } from '@src/backendTasks/readCsvFile'; +import { registerReadMaps } from '@src/backendTasks/readMaps'; +import { registerReadRMXPEvents } from '@src/backendTasks/readRMXPEvents'; +import { registerReadRMXPMapInfo } from '@src/backendTasks/readRMXPMapInfo'; +import { registerRequestJson } from '@src/backendTasks/requestJson'; +import { registerSaveCompilationLogs } from '@src/backendTasks/saveCompilationLogs'; +import { registerSaveEventTree } from '@src/backendTasks/saveEventTree'; +import { registerSaveMapInfo } from '@src/backendTasks/saveMapInfo'; +import { registerSaveRMXPMapInfo } from '@src/backendTasks/saveRMXPMapInfo'; +import { registerSaveTextInfos } from '@src/backendTasks/saveTextInfos'; +import { registerStartCompilation } from '@src/backendTasks/startCompilation'; +import { registerStartupStudioFile, startupFiles } from '@src/backendTasks/startupStudioFile'; +import { registerSynchronizeLanguage } from '@src/backendTasks/synchronizeLanguage'; +import { registerUpdateTextInfos } from '@src/backendTasks/updateTextInfos'; +import windowManager from '@src/backendTasks/windowManager'; +import { registerElectronProtocolWhenAppRead } from '@utils/electronProtocol'; import crypto from 'crypto'; -import path from 'path'; import { app, BrowserWindow, ipcMain, shell } from 'electron'; import log, { FileTransport, PathVariables } from 'electron-log'; -import MenuBuilder from './menu'; -import windowManager from '@src/backendTasks/windowManager'; import { autoUpdater } from 'electron-updater'; -import { getPSDKBinariesPath, getPSDKVersion } from '../services/getPSDKVersion'; -import { getLastPSDKVersion } from '../services/getLastPSDKVersion'; -import { updatePSDK } from '../services/updatePSDK'; -import { startPSDK, startPSDKDebug, startPSDKTags, startPSDKWorldmap } from '../services/startPSDK'; -import { registerElectronProtocolWhenAppRead } from '@utils/electronProtocol'; -import { registerGetStudioVersion } from '../backendTasks/getStudioVersion'; +import path from 'path'; +import { registerChooseFile } from '../backendTasks/chooseFile'; +import { registerChooseFolder } from '../backendTasks/chooseFolder'; import { registerChooseProjectFileToOpen } from '../backendTasks/chooseProjectFileToOpen'; -import { registerWriteProjectMetadata } from '../backendTasks/writeProjectMetadata'; -import { registerReadProjectMetadata } from '../backendTasks/readProjectMetadata'; +import { registerConfigureNewProject } from '../backendTasks/configureNewProject'; +import { registerCopyFile } from '../backendTasks/copyFile'; +import { registerExtractNewProject } from '../backendTasks/extractNewProject'; +import { registerFileExists } from '../backendTasks/fileExists'; +import { registerGetStudioVersion } from '../backendTasks/getStudioVersion'; +import { registerMigrateData } from '../backendTasks/migrateData'; +import { registerProjectStudioFile } from '../backendTasks/projectStudioFile'; import { registerReadProjectConfigs } from '../backendTasks/readProjectConfigs'; import { registerReadProjectData } from '../backendTasks/readProjectData'; +import { registerReadProjectMetadata } from '../backendTasks/readProjectMetadata'; import { registerReadProjectTexts } from '../backendTasks/readProjectTexts'; -import { registerMigrateData } from '../backendTasks/migrateData'; -import { registerFileExists } from '../backendTasks/fileExists'; -import { registerChooseFolder } from '../backendTasks/chooseFolder'; -import { registerExtractNewProject } from '../backendTasks/extractNewProject'; -import { registerConfigureNewProject } from '../backendTasks/configureNewProject'; -import { registerSaveProjectData } from '../backendTasks/saveProjectData'; import { registerSaveProjectConfigs } from '../backendTasks/saveProjectConfigs'; +import { registerSaveProjectData } from '../backendTasks/saveProjectData'; import { registerSaveProjectTexts } from '../backendTasks/saveProjectTexts'; -import { registerProjectStudioFile } from '../backendTasks/projectStudioFile'; -import { registerChooseFile } from '../backendTasks/chooseFile'; import { registerShowItemInFolder } from '../backendTasks/showFileInFolder'; -import { registerCopyFile } from '../backendTasks/copyFile'; -import { registerUpdateTextInfos } from '@src/backendTasks/updateTextInfos'; -import { registerSaveTextInfos } from '@src/backendTasks/saveTextInfos'; -import { registerReadCsvFile } from '@src/backendTasks/readCsvFile'; -import { registerOpenStudioLogsFolder } from '@src/backendTasks/openStudioLogsFolder'; -import { registerCheckMapsModified } from '@src/backendTasks/checkMapsModified'; -import { registerConvertTiledMapToTileMetadata } from '@src/backendTasks/convertTiledMapToTileMetadata'; -import { registerSaveMapInfo } from '@src/backendTasks/saveMapInfo'; -import { registerSaveEventTree } from '@src/backendTasks/saveEventTree'; -import { registerStartupStudioFile, startupFiles } from '@src/backendTasks/startupStudioFile'; -import { registerGetFilePathsFromFolder } from '@src/backendTasks/getFilePathsFromFolder'; -import { registerCopyTiledFiles } from '@src/backendTasks/copyTiledFiles'; -import { registerRMXP2StudioMapsSync } from '@src/backendTasks/RMXP2StudioMapsSync'; -import { registerReadRMXPMapInfo } from '@src/backendTasks/readRMXPMapInfo'; -import { registerReadRMXPMap } from '@src/backendTasks/readRMXPMap'; -import { registerReadMaps } from '@src/backendTasks/readMaps'; -import { registerSaveRMXPMapInfo } from '@src/backendTasks/saveRMXPMapInfo'; -import { registerOpenTiled } from '@src/backendTasks/openTiled'; -import { registerDownloadFile } from '@src/backendTasks/downloadFile'; -import { registerRequestJson } from '@src/backendTasks/requestJson'; -import { registerCheckDownloadNewProject } from '@src/backendTasks/checkDownloadNewProject'; -import { registerGeneratingMapOverview } from '@src/backendTasks/generatingMapOverview'; -import { registerOpenCompilationWindow } from '@src/backendTasks/openCompilationWindow'; -import { registerGetCompilationConfig } from '@src/backendTasks/getCompilationConfig'; -import { registerStartCompilation } from '@src/backendTasks/startCompilation'; -import { registerSaveCompilationLogs } from '@src/backendTasks/saveCompilationLogs'; -import { registerSynchronizeLanguage } from '@src/backendTasks/synchronizeLanguage'; -import { registerReadRMXPEvents } from '@src/backendTasks/readRMXPEvents'; -import { registerConvertRMXPEventsToStudioEvents } from '@src/backendTasks/convertRMXPEventsToStudioEvents'; +import { registerWriteProjectMetadata } from '../backendTasks/writeProjectMetadata'; +import { getLastPSDKVersion } from '../services/getLastPSDKVersion'; +import { getPSDKBinariesPath, getPSDKVersion } from '../services/getPSDKVersion'; +import { startPSDK, startPSDKDebug, startPSDKWorldmap } from '../services/startPSDK'; +import { updatePSDK } from '../services/updatePSDK'; +import MenuBuilder from './menu'; // This allows TypeScript to pick up the magic constants that's auto-generated by Forge's Webpack // plugin that tells the Electron app where to look for the Webpack-bundled app code (depending on @@ -158,7 +156,6 @@ ipcMain.on('get-last-psdk-version', getLastPSDKVersion); ipcMain.on('update-psdk', updatePSDK); ipcMain.on('start-psdk', (_, projectPath: string) => startPSDK(projectPath)); ipcMain.on('start-psdk-debug', (_, projectPath: string) => startPSDKDebug(projectPath)); -ipcMain.on('start-psdk-tags', (_, projectPath: string) => startPSDKTags(projectPath)); ipcMain.on('start-psdk-worldmap', (_, projectPath: string) => startPSDKWorldmap(projectPath)); ipcMain.on('external-window', (_, arg) => shell.openExternal(arg)); registerGetStudioVersion(ipcMain); @@ -191,11 +188,9 @@ registerSaveEventTree(ipcMain); registerStartupStudioFile(ipcMain); registerGetFilePathsFromFolder(ipcMain); registerCopyTiledFiles(ipcMain); -registerRMXP2StudioMapsSync(ipcMain); registerReadRMXPMapInfo(ipcMain); -registerReadRMXPMap(ipcMain); -registerReadMaps(ipcMain); registerSaveRMXPMapInfo(ipcMain); +registerReadMaps(ipcMain); registerOpenTiled(ipcMain); registerDownloadFile(ipcMain); registerRequestJson(ipcMain); diff --git a/src/models/entities/project.ts b/src/models/entities/project.ts index a5d74aaf4..ccbcc2847 100644 --- a/src/models/entities/project.ts +++ b/src/models/entities/project.ts @@ -10,7 +10,6 @@ export const PROJECT_VALIDATOR = z.object({ title: z.string(), studioVersion: z.string(), iconPath: z.string().default('graphics/icons/game.png'), - isTiledMode: z.boolean().nullable().default(null), languagesTranslation: z.array(PROJECT_LANGUAGE_TRANSLATION_VALIDATOR), }); export type StudioProject = z.infer; @@ -23,12 +22,10 @@ export const createProjectStudio = ( title: string, studioVersion: string, iconPath: string, - isTiledMode: boolean, languagesTranslation: StudioProjectLanguageTranslation[], ): StudioProject => ({ title, studioVersion, iconPath, - isTiledMode, languagesTranslation, }); diff --git a/src/preload.ts b/src/preload.ts index efe3e584c..ed11fccda 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -1,55 +1,54 @@ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-nocheck -import { ipcRenderer, contextBridge, webFrame, IpcRendererEvent, webUtils } from 'electron'; -import { BackendTaskWithGenericError, BackendTaskWithGenericErrorAndNoProgress, GenericBackendProgress, defineBackendTask } from '@utils/BackendTask'; -import type { PSDKVersion } from '@services/getPSDKVersion'; import type { StudioShortcut } from '@hooks/useShortcuts'; -import type { ChooseProjectFileToOpenInput } from './backendTasks/chooseProjectFileToOpen'; -import type { ConfigureNewProjectInput } from './backendTasks/configureNewProject'; -import type { CopyFileInput } from './backendTasks/copyFile'; -import type { ProjectConfigsFromBackEnd, ReadProjectConfigsInput } from './backendTasks/readProjectConfigs'; -import type { ProjectDataFromBackEnd, ReadProjectDataInput } from './backendTasks/readProjectData'; -import type { CheckMapModifiedInput, CheckMapModifiedOutput } from './backendTasks/checkMapsModified'; -import type { ProjectText } from './GlobalStateProvider'; +import type { PSDKVersion } from '@services/getPSDKVersion'; +import { BackendTaskWithGenericError, BackendTaskWithGenericErrorAndNoProgress, GenericBackendProgress, defineBackendTask } from '@utils/BackendTask'; import type { LogRendererType } from '@utils/logRenderer'; import * as logRenderer from '@utils/logRenderer'; -import type { SaveTextInfosInput } from './backendTasks/saveTextInfos'; -import type { ReadCsvFileInput } from './backendTasks/readCsvFile'; -import type { UpdateTextInfosInput } from './backendTasks/updateTextInfos'; -import type { ShowItemInFolderInput } from './backendTasks/showFileInFolder'; +import { IpcRendererEvent, contextBridge, ipcRenderer, webFrame, webUtils } from 'electron'; +import type { CheckDownloadNewProjectInput, CheckDownloadNewProjectOutput } from './backendTasks/checkDownloadNewProject'; +import type { CheckMapModifiedInput, CheckMapModifiedOutput } from './backendTasks/checkMapsModified'; import type { ChooseFileInput, ChooseFileOutput } from './backendTasks/chooseFile'; -import type { ProjectStudioFileInput, ProjectStudioFileOutput } from './backendTasks/projectStudioFile'; -import type { SaveProjectTextsInput } from './backendTasks/saveProjectTexts'; -import type { SaveProjectConfigInput } from './backendTasks/saveProjectConfigs'; -import type { ExtractNewProjectInput } from './backendTasks/extractNewProject'; -import type { MigrateDataInput, MigrateDataOutput } from './backendTasks/migrateData'; -import type { ReadProjectTextInput } from './backendTasks/readProjectTexts'; -import type { ReadProjectMetadataInput, ReadProjectMetadataOutput } from './backendTasks/readProjectMetadata'; -import type { WriteProjectMetadataInput } from './backendTasks/writeProjectMetadata'; -import type { GetStudioVersionOutput } from './backendTasks/getStudioVersion'; +import type { ChooseProjectFileToOpenInput } from './backendTasks/chooseProjectFileToOpen'; +import type { ConfigureNewProjectInput } from './backendTasks/configureNewProject'; +import type { RMXPEventsToStudioEventsInput, RMXPEventsToStudioEventsOutput } from './backendTasks/convertRMXPEventsToStudioEvents'; import type { ConvertTMXInput } from './backendTasks/convertTiledMapToTileMetadata'; -import type { SaveMapInfoInput } from './backendTasks/saveMapInfo'; -import type { SaveEventTreeInput } from './backendTasks/saveEventTree'; -import type { StartupStudioFileOutput } from './backendTasks/startupStudioFile'; -import type { GetFilePathsFromFolderInput, GetFilePathsFromFolderOutput } from './backendTasks/getFilePathsFromFolder'; +import type { CopyFileInput } from './backendTasks/copyFile'; import type { CopyTiledFilesInput, CopyTiledFilesOutput } from './backendTasks/copyTiledFiles'; -import type { RMXP2StudioMapsSyncInput } from './backendTasks/RMXP2StudioMapsSync'; -import type { ReadRMXPMapInfoInput, ReadRMXPMapInfoOutput } from './backendTasks/readRMXPMapInfo'; -import type { ReadRMXPMapInput, ReadRMXPMapOutput } from './backendTasks/readRMXPMap'; -import type { SaveRMXPMapInfoInput } from './backendTasks/saveRMXPMapInfo'; -import type { OpenTiledPayload } from './backendTasks/openTiled'; import type { DownloadFileInput } from './backendTasks/downloadFile'; -import type { RequestJsonInput, RequestJsonOutput } from './backendTasks/requestJson'; -import type { CheckDownloadNewProjectInput, CheckDownloadNewProjectOutput } from './backendTasks/checkDownloadNewProject'; +import type { ExtractNewProjectInput } from './backendTasks/extractNewProject'; import type { FileExistsInput, FileExistsOutput } from './backendTasks/fileExists'; import type { GeneratingMapOverviewInput } from './backendTasks/generatingMapOverview'; -import type { OpenCompilationWindowInput } from './backendTasks/openCompilationWindow'; import type { GetCompilationConfigOutput } from './backendTasks/getCompilationConfig'; -import type { StartCompilationInput, StartCompilationOutput } from './backendTasks/startCompilation'; +import type { GetFilePathsFromFolderInput, GetFilePathsFromFolderOutput } from './backendTasks/getFilePathsFromFolder'; +import type { GetStudioVersionOutput } from './backendTasks/getStudioVersion'; +import type { MigrateDataInput, MigrateDataOutput } from './backendTasks/migrateData'; +import type { OpenCompilationWindowInput } from './backendTasks/openCompilationWindow'; +import type { OpenTiledPayload } from './backendTasks/openTiled'; +import type { ProjectStudioFileInput, ProjectStudioFileOutput } from './backendTasks/projectStudioFile'; +import type { ReadCsvFileInput } from './backendTasks/readCsvFile'; +import type { ProjectConfigsFromBackEnd, ReadProjectConfigsInput } from './backendTasks/readProjectConfigs'; +import type { ProjectDataFromBackEnd, ReadProjectDataInput } from './backendTasks/readProjectData'; +import type { ReadProjectMetadataInput, ReadProjectMetadataOutput } from './backendTasks/readProjectMetadata'; +import type { ReadProjectTextInput } from './backendTasks/readProjectTexts'; +import type { ReadRMXPEventInput, ReadRMXPEventOutput } from './backendTasks/readRMXPEvents'; +import type { ReadRMXPMapInput, ReadRMXPMapOutput } from './backendTasks/readRMXPMap'; +import type { ReadRMXPMapInfoInput, ReadRMXPMapInfoOutput } from './backendTasks/readRMXPMapInfo'; +import type { RequestJsonInput, RequestJsonOutput } from './backendTasks/requestJson'; import type { SaveCompilationLogsInput } from './backendTasks/saveCompilationLogs'; +import type { SaveEventTreeInput } from './backendTasks/saveEventTree'; +import type { SaveMapInfoInput } from './backendTasks/saveMapInfo'; +import type { SaveProjectConfigInput } from './backendTasks/saveProjectConfigs'; +import type { SaveProjectTextsInput } from './backendTasks/saveProjectTexts'; +import type { SaveRMXPMapInfoInput } from './backendTasks/saveRMXPMapInfo'; +import type { SaveTextInfosInput } from './backendTasks/saveTextInfos'; +import type { ShowItemInFolderInput } from './backendTasks/showFileInFolder'; +import type { StartCompilationInput, StartCompilationOutput } from './backendTasks/startCompilation'; +import type { StartupStudioFileOutput } from './backendTasks/startupStudioFile'; import type { SynchronizeLanguageInput } from './backendTasks/synchronizeLanguage'; -import type { ReadRMXPEventInput, ReadRMXPEventOutput } from './backendTasks/readRMXPEvents'; -import type { RMXPEventsToStudioEventsInput, RMXPEventsToStudioEventsOutput } from './backendTasks/convertRMXPEventsToStudioEvents'; +import type { UpdateTextInfosInput } from './backendTasks/updateTextInfos'; +import type { WriteProjectMetadataInput } from './backendTasks/writeProjectMetadata'; +import type { ProjectText } from './GlobalStateProvider'; contextBridge.exposeInMainWorld('api', { isDev: process.env.NODE_ENV === 'development', @@ -107,9 +106,6 @@ contextBridge.exposeInMainWorld('api', { startPSDKDebug: (projectPath: string) => { ipcRenderer.send('start-psdk-debug', projectPath); }, - startPSDKTags: (projectPath: string) => { - ipcRenderer.send('start-psdk-tags', projectPath); - }, startPSDKWorldmap: (projectPath: string) => { ipcRenderer.send('start-psdk-worldmap', projectPath); }, @@ -146,11 +142,10 @@ contextBridge.exposeInMainWorld('api', { startupStudioFile: defineBackendTask(ipcRenderer, 'startup-studio-file'), getFilePathsFromFolder: defineBackendTask(ipcRenderer, 'get-file-paths-from-folder'), copyTiledFiles: defineBackendTask(ipcRenderer, 'copy-tiled-files'), - RMXP2StudioMapsSync: defineBackendTask(ipcRenderer, 'rmxp-to-studio-maps-sync'), readRMXPMapInfo: defineBackendTask(ipcRenderer, 'read-rmxp-map-info'), readRMXPMap: defineBackendTask(ipcRenderer, 'read-rmxp-map'), - readMaps: defineBackendTask(ipcRenderer, 'read-maps'), saveRMXPMapInfo: defineBackendTask(ipcRenderer, 'save-rmxp-map-info'), + readMaps: defineBackendTask(ipcRenderer, 'read-maps'), openTiled: defineBackendTask(ipcRenderer, 'open-tiled'), downloadFile: defineBackendTask(ipcRenderer, 'download-file'), requestJson: defineBackendTask(ipcRenderer, 'request-json'), @@ -202,12 +197,11 @@ declare global { updatePSDK: ( currentVersion: number, onStatusUpdate: (current: number, total: number, version: PSDKVersion) => void, - onDone: (success: boolean) => void + onDone: (success: boolean) => void, ) => void; unregisterPSDKUpdateEvents: () => void; startPSDK: (projectPath: string) => void; startPSDKDebug: (projectPath: string) => void; - startPSDKTags: (projectPath: string) => void; startPSDKWorldmap: (projectPath: string) => void; platform: string; externalWindow: (link: string) => void; @@ -242,11 +236,10 @@ declare global { startupStudioFile: BackendTaskWithGenericErrorAndNoProgress; getFilePathsFromFolder: BackendTaskWithGenericErrorAndNoProgress; copyTiledFiles: BackendTaskWithGenericErrorAndNoProgress; - RMXP2StudioMapsSync: BackendTaskWithGenericErrorAndNoProgress; readRMXPMapInfo: BackendTaskWithGenericErrorAndNoProgress; readRMXPMap: BackendTaskWithGenericErrorAndNoProgress; - readMaps: BackendTaskWithGenericErrorAndNoProgress; saveRMXPMapInfo: BackendTaskWithGenericErrorAndNoProgress; + readMaps: BackendTaskWithGenericErrorAndNoProgress; openTiled: BackendTaskWithGenericErrorAndNoProgress; downloadFile: BackendTaskWithGenericError; requestJson: BackendTaskWithGenericErrorAndNoProgress; diff --git a/src/services/startPSDK.ts b/src/services/startPSDK.ts index 50b7fe448..54e4d97eb 100644 --- a/src/services/startPSDK.ts +++ b/src/services/startPSDK.ts @@ -1,9 +1,9 @@ -import { spawn } from 'child_process'; -import path from 'path'; import { generateGameLinuxFileContent, generateGameMacFileContent, generatePSDKBatFileContent } from '@services/generatePSDKBatFileContent'; -import { writeFileSync, existsSync, readFileSync, chmodSync } from 'fs'; import { getPSDKBinariesPath } from '@services/getPSDKVersion'; +import { spawn } from 'child_process'; import log from 'electron-log'; +import { chmodSync, existsSync, readFileSync, writeFileSync } from 'fs'; +import path from 'path'; export const getSpawnArgs = (projectPath: string, ...args: string[]): [string, string[]] => { if (process.platform === 'win32') { @@ -48,12 +48,12 @@ const generateBootLoadContent = (projectPath: string) => { return generatePSDKBatFileContent(); }; -export const RMXP2StudioSafetyNet = (projectPath: string) => { +export const ensureBootLoadFile = (projectPath: string) => { const psdkBatPath = path.join(projectPath, bootLoadFilename()); const psdkBatContent = generateBootLoadContent(projectPath); const realPsdkBatContent = existsSync(psdkBatPath) && readFileSync(psdkBatPath).toString('utf-8'); if (realPsdkBatContent !== psdkBatContent) writeFileSync(psdkBatPath, psdkBatContent); - // eslint-disable-next-line no-octal + if (process.platform !== 'win32') chmodSync(psdkBatPath, 0o755); const gameRbPath = path.join(projectPath, 'Game.rb'); @@ -87,7 +87,7 @@ const childProcessFn = (projectPath: string, command: string, spawnArgs: string[ export const startPSDK = (projectPath: string) => { if (!canLaunchPSDK()) return; - RMXP2StudioSafetyNet(projectPath); + ensureBootLoadFile(projectPath); const studioPath = process.cwd(); try { @@ -104,7 +104,7 @@ export const startPSDK = (projectPath: string) => { const startPSDKWithArgs = (projectPath: string, ...args: string[]) => { if (!canLaunchPSDK()) return; - RMXP2StudioSafetyNet(projectPath); + ensureBootLoadFile(projectPath); const studioPath = process.cwd(); try { @@ -119,5 +119,4 @@ const startPSDKWithArgs = (projectPath: string, ...args: string[]) => { }; export const startPSDKDebug = (projectPath: string) => startPSDKWithArgs(projectPath, 'debug'); -export const startPSDKTags = (projectPath: string) => startPSDKWithArgs(projectPath, '--tags'); export const startPSDKWorldmap = (projectPath: string) => startPSDKWithArgs(projectPath, '--worldmap'); diff --git a/src/utils/generateSelectedIdentifier.ts b/src/utils/generateSelectedIdentifier.ts index 4f95486d3..dc6694c15 100644 --- a/src/utils/generateSelectedIdentifier.ts +++ b/src/utils/generateSelectedIdentifier.ts @@ -61,21 +61,7 @@ const getSelectedIdentifier = ( return undefined; }; -const getMapLinkIdentifier = (selectedFromStorage: SelectedDataIdentifier, preState: PreGlobalState, validMaps: number[]) => { - const expectedMapId = Number(selectedFromStorage.mapLink); - const maps = Object.values(preState.projectData.maps); - - if (maps.find(({ id }) => id === expectedMapId)) return expectedMapId.toString(); - - return ( - maps - .filter(({ id }) => validMaps.includes(id)) - .sort((a, b) => a.id - b.id)[0] - ?.id.toString() || '__undef__' - ); -}; - -const getMapLinkIdentifierV2 = (selectedFromStorage: SelectedDataIdentifier, preState: PreGlobalState) => { +const getMapLinkIdentifierV2 =(selectedFromStorage: SelectedDataIdentifier, preState: PreGlobalState) => { const identifier = selectedFromStorage.mapLink; const mapLinks = Object.values(preState.projectData.mapLinks); @@ -97,10 +83,6 @@ const getTextInfoIdentifier = (selectedFromStorage: SelectedDataIdentifier, text export const generateSelectedIdentifier = (preState: PreGlobalState): SelectedDataIdentifier => { const projectData = preState.projectData; const selectedFromStorage = getSelectedIdentifierFromStorage(preState); - const validMaps = Object.values(projectData.zones) - .filter((zone) => zone.isFlyAllowed && !zone.isWarpDisallowed) - .flatMap((zone) => zone.maps); - const isRMXPMode = !preState.projectStudio.isTiledMode; return { pokemon: getSelectedIdentifier(preState, selectedFromStorage, 'pokemon', 'pokemon') || { specie: firstById(projectData.pokemon), @@ -115,7 +97,7 @@ export const generateSelectedIdentifier = (preState: PreGlobalState): SelectedDa ability: getSelectedIdentifier(preState, selectedFromStorage, 'ability', 'abilities') || firstByNameUsingTextId(projectData.abilities, preState), group: getSelectedIdentifier(preState, selectedFromStorage, 'group', 'groups') || firstById(projectData.groups), dex: getSelectedIdentifier(preState, selectedFromStorage, 'dex', 'dex') || firstById(projectData.dex), - mapLink: isRMXPMode ? getMapLinkIdentifier(selectedFromStorage, preState, validMaps) : getMapLinkIdentifierV2(selectedFromStorage, preState), + mapLink: getMapLinkIdentifierV2(selectedFromStorage, preState), textInfo: getTextInfoIdentifier(selectedFromStorage, preState.textInfos), map: getSelectedIdentifier(preState, selectedFromStorage, 'map', 'maps') || firstById(projectData.maps), nature: getSelectedIdentifier(preState, selectedFromStorage, 'nature', 'natures') || firstByName(projectData.natures, preState), diff --git a/src/utils/loaderContext.tsx b/src/utils/loaderContext.tsx index 8abdfc01f..01e44a733 100644 --- a/src/utils/loaderContext.tsx +++ b/src/utils/loaderContext.tsx @@ -17,9 +17,10 @@ type LoaderErrorTitle = | 'loading_project_error' | 'updating_psdk_error' | 'importing_tiled_maps_error' + | 'assigning_tiled_maps_error' | 'updating_maps_error' | 'compilation_project_error'; -type LoaderSuccessTitle = 'importing_tiled_maps_success' | 'update_maps'; +type LoaderSuccessTitle = 'importing_tiled_maps_success' | 'assigning_tiled_maps_success' | 'update_maps'; type LoaderState = { thingInProgress: LoaderTitle; diff --git a/src/views/components/EndSupportRMXPMapsBanner.tsx b/src/views/components/EndSupportRMXPMapsBanner.tsx deleted file mode 100644 index 8e0ace37b..000000000 --- a/src/views/components/EndSupportRMXPMapsBanner.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import { WarningButton } from './buttons'; -import { useTranslation } from 'react-i18next'; -import IconInfo from '@assets/icons/notification/info.svg'; -import IconClose from '@assets/icons/global/clear-icon.svg'; -import { useDialogsRef } from '@root/src/hooks/useDialogsRef'; -import { DashboardEditorAndDeletionKeys, DashboardEditorOverlay } from './dashboard/editors/DashboardEditorOverlay'; -import { useLocation } from 'react-router'; -import styled from 'styled-components'; -import React, { useEffect, useRef } from 'react'; -import { StudioProject } from '@root/src/models/entities/project'; - -type EndSupportRMXPMapsBannerContainerProps = { - fixPosition: number; -}; - -export const EndSupportRMXPMapsBannerContainer = styled.div` - position: absolute; - bottom: 24px; - left: calc(50% + 32px + ${({ fixPosition }) => `${fixPosition / 2}px`}); - transform: translate(-50%, 0); - display: grid; - grid-template-columns: 20px auto 93px 32px; - grid-gap: 8px; - padding: 8px 8px 8px 16px; - height: 54px; - align-items: center; - ${({ theme }) => theme.fonts.normalRegular} - background-color: ${({ theme }) => theme.colors.warningSoft}; - border: 1px solid ${({ theme }) => theme.colors.warningSoft}; - backdrop-filter: blur(12px); - border-radius: 8px; - width: 1024px; - box-sizing: border-box; - user-select: none; - visibility: hidden; - z-index: 98; - - @media ${({ theme }) => theme.breakpoints.smallScreen} { - width: 504px; - height: 108px; - } - - .info-icon { - display: flex; - align-items: center; - color: ${({ theme }) => theme.colors.warningBase}; - - svg { - width: 20px; - height: auto; - } - } - - .close-icon { - display: flex; - align-items: center; - justify-content: center; - color: ${({ theme }) => theme.colors.warningBase}; - height: 32px; - - &:hover { - cursor: pointer; - } - - svg { - width: 12px; - height: auto; - } - } - - ${WarningButton} { - padding: 0px 12px; - height: 32px; - ${({ theme }) => theme.fonts.normalSmall}; - font-weight: 500; - } -`; - -export const EndSupportRMXPMapsBanner = () => { - const dialogsRef = useDialogsRef(); - const bannerRef = useRef(null); - const { t } = useTranslation(); - const location = useLocation(); - - const updateBannerVisibility = (visibility: 'hidden' | 'visible') => { - if (!bannerRef.current) return; - - bannerRef.current.style.visibility = visibility; - }; - - const fixPosition = () => { - if (location.pathname.match(/^\/(dashboard\/|database|poc|settings)/)) return 216; - if (location.pathname.startsWith('/world')) return 318; - - return 0; - }; - - useEffect(() => { - const listener = (e: Event) => { - const event = e as CustomEvent<{ projectStudio: StudioProject }>; - const projectStudio = event.detail.projectStudio; - updateBannerVisibility(projectStudio?.isTiledMode ? 'hidden' : 'visible'); - }; - - window.addEventListener('project-opened', listener); - return () => window.removeEventListener('project-opened', listener); - }, []); - - return ( - <> - -
- -
- {t('end_of_rmxp_support')} - dialogsRef.current?.openDialog('studio_mode_message_box', true)}>{t('button_use_tiled')} -
updateBannerVisibility('hidden')}> - -
-
- - - ); -}; diff --git a/src/views/components/buttons/LoadProjectButton/LoadProjectButton.tsx b/src/views/components/buttons/LoadProjectButton/LoadProjectButton.tsx index 1e365fb68..8d6349a01 100644 --- a/src/views/components/buttons/LoadProjectButton/LoadProjectButton.tsx +++ b/src/views/components/buttons/LoadProjectButton/LoadProjectButton.tsx @@ -1,9 +1,10 @@ -import React, { ReactNode, useEffect } from 'react'; +import React, { ReactNode, useEffect, useRef, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { SecondaryButton } from '../GenericButtons'; import { useLoaderRef } from '@utils/loaderContext'; import { useProjectLoad } from '@hooks/useProjectLoad'; import { useTranslation } from 'react-i18next'; +import { RmxpMigrationDialog } from '@components/home/RmxpMigrationDialog'; type LoadProjectButtonProps = { children: ReactNode }; @@ -12,6 +13,8 @@ export const LoadProjectButton = ({ children }: LoadProjectButtonProps) => { const loaderRef = useLoaderRef(); const projectLoad = useProjectLoad(); const { t } = useTranslation(); + const continueRef = useRef<(() => void) | null>(null); + const [showRmxpDialog, setShowRmxpDialog] = useState(false); const handleClick = async (projectDirName?: string) => { projectLoad( @@ -21,7 +24,11 @@ export const LoadProjectButton = ({ children }: LoadProjectButtonProps) => { navigate('/dashboard'); }, ({ errorMessage }) => loaderRef.current.setError('loading_project_error', errorMessage), - (count) => loaderRef.current.setError('loading_project_error', t('integrity_message', { count }), true) + (count) => loaderRef.current.setError('loading_project_error', t('integrity_message', { count }), true), + (onContinue) => { + continueRef.current = onContinue; + setShowRmxpDialog(true); + }, ); }; @@ -32,10 +39,20 @@ export const LoadProjectButton = ({ children }: LoadProjectButtonProps) => { ({ projectPath }) => { if (projectPath) handleClick(projectPath); }, - () => {} + () => {}, ), - [] + [], ); - return handleClick()}>{children}; + return ( + <> + handleClick()}>{children} + {showRmxpDialog && ( + continueRef.current?.()} + closeDialog={() => setShowRmxpDialog(false)} + /> + )} + + ); }; diff --git a/src/views/components/buttons/NewProjectButton.tsx b/src/views/components/buttons/NewProjectButton.tsx index bb8578923..4dae3686b 100644 --- a/src/views/components/buttons/NewProjectButton.tsx +++ b/src/views/components/buttons/NewProjectButton.tsx @@ -38,7 +38,8 @@ export const NewProjectButton = ({ newProjectData, disabled, closeDialog }: NewP (count) => { closeDialog(); loaderRef.current.setError('loading_project_error', t('integrity_message', { count }), true); - } + }, + () => {} ); }, ({ errorMessage }) => { diff --git a/src/views/components/buttons/PlayButton.tsx b/src/views/components/buttons/PlayButton.tsx index bbfd7e4fd..42a129ac2 100644 --- a/src/views/components/buttons/PlayButton.tsx +++ b/src/views/components/buttons/PlayButton.tsx @@ -118,7 +118,6 @@ export const PlayButton = () => {
startPSDKAndCloseMenu(window.api.startPSDK)}>{t('play_release_mode')} startPSDKAndCloseMenu(window.api.startPSDKDebug)}>{t('play_debug_mode')} - {!state.projectStudio.isTiledMode && startPSDKAndCloseMenu(window.api.startPSDKTags)}>{t('play_tags_mode')}} startPSDKAndCloseMenu(window.api.startPSDKWorldmap)}>{t('play_worldmap_mode')}
diff --git a/src/views/components/dashboard/editors/DashboardEditorOverlay.tsx b/src/views/components/dashboard/editors/DashboardEditorOverlay.tsx index de3891acc..7149b1850 100644 --- a/src/views/components/dashboard/editors/DashboardEditorOverlay.tsx +++ b/src/views/components/dashboard/editors/DashboardEditorOverlay.tsx @@ -3,9 +3,8 @@ import { defineEditorOverlay } from '@components/editor/EditorOverlayV2'; import { DialogRefData } from '@hooks/useDialogsRef'; import { assertUnreachable } from '@utils/assertUnreachable'; import React from 'react'; -import { DashboardStudioModeMessageBox } from './DashboardStudioModeMessageBox'; -export type DashboardEditorAndDeletionKeys = 'studio_mode_message_box' | 'create_playable_game'; +export type DashboardEditorAndDeletionKeys = 'create_playable_game'; export type DashboardDialogsRef = React.RefObject | null>; /** @@ -16,8 +15,6 @@ export const DashboardEditorOverlay = defineEditorOverlay { switch (dialogToShow) { - case 'studio_mode_message_box': - return ; case 'create_playable_game': return ; default: diff --git a/src/views/components/dashboard/editors/DashboardStudioModeMessageBox.tsx b/src/views/components/dashboard/editors/DashboardStudioModeMessageBox.tsx deleted file mode 100644 index c114db8a3..000000000 --- a/src/views/components/dashboard/editors/DashboardStudioModeMessageBox.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useLoaderRef } from '@utils/loaderContext'; -import { PrimaryButton } from '@components/buttons'; -import { useProjectStudio } from '@hooks/useProjectStudio'; -import { useTranslation } from 'react-i18next'; -import { useProjectLoad } from '@hooks/useProjectLoad'; -import { - MessageBoxActionContainer, - MessageBoxCancelLink, - MessageBoxContainer, - MessageBoxIconContainer, - MessageBoxTextContainer, - MessageBoxTitleIconContainer, -} from '@components/MessageBoxContainer'; -import theme from '@src/AppTheme'; -import { BaseIcon } from '@components/icons/BaseIcon'; -import { useProjectSave } from '@hooks/useProjectSave'; - -type DashboardStudioModeMessageBoxState = 'select_mode' | 'save' | 'reload_project'; - -type DashboardStudioModeMessageBoxProps = { - closeDialog: () => void; -}; - -export const DashboardStudioModeMessageBox = ({ closeDialog }: DashboardStudioModeMessageBoxProps) => { - const loaderRef = useLoaderRef(); - const { save } = useProjectSave(); - const projectLoad = useProjectLoad(); - const [state, setState] = useState('select_mode'); - const [mode, setMode] = useState<'tiled' | 'rmxp' | undefined>(undefined); - const { projectStudioValues: projectStudio, setProjectStudioValues: setProjectStudio, state: globalState } = useProjectStudio(); - const { t } = useTranslation(); - - useEffect(() => { - switch (state) { - case 'select_mode': - if (mode) { - setProjectStudio({ ...projectStudio, isTiledMode: mode === 'tiled' }); - setState('save'); - } - return; - case 'save': - return save( - () => setState('reload_project'), - ({ errorMessage }) => { - loaderRef.current.setError('saving_project_error', errorMessage); - closeDialog(); - } - ); - case 'reload_project': - return projectLoad( - { projectDirName: globalState.projectPath! }, - () => { - // we wait the end of the close dialog animation to close the loader - setTimeout(() => loaderRef.current.close(), 200); - closeDialog(); - }, - ({ errorMessage }) => loaderRef.current.setError('loading_project_error', errorMessage), - (count) => loaderRef.current.setError('loading_project_error', t('integrity_message', { count }), true) - ); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [state, mode]); - - return ( - - - - - -

{t('title_studio_mode_message_box')}

-
- -

{t('message_studio_mode_message_box')}

-

- {t('warning_message')} -

-
- - (projectStudio?.isTiledMode === null ? setMode('rmxp') : closeDialog())}> - {t('cancel')} - - setMode('tiled')}>{t('button_use_tiled')} - -
- ); -}; diff --git a/src/views/components/database/pokemon/pokemonDataBlock/EvolutionDataBlock.tsx b/src/views/components/database/pokemon/pokemonDataBlock/EvolutionDataBlock.tsx index 5511bfe41..4b7702b73 100644 --- a/src/views/components/database/pokemon/pokemonDataBlock/EvolutionDataBlock.tsx +++ b/src/views/components/database/pokemon/pokemonDataBlock/EvolutionDataBlock.tsx @@ -1,11 +1,11 @@ -import { useGetEntityNameText } from '@utils/ReadingProjectText'; +import { CONTROL, useKeyPress } from '@hooks/useKeyPress'; import { useProjectPokemon } from '@hooks/useProjectData'; +import { usePokemonShortcutNavigation } from '@hooks/useShortcutNavigation'; +import { useGetEntityNameText } from '@utils/ReadingProjectText'; import React from 'react'; import { useTranslation } from 'react-i18next'; import { DataBlockWithTitlePagination, DataFieldsetField, DataGrid } from '../../dataBlocks'; import { PokemonDataProps } from '../PokemonDataPropsInterface'; -import { CONTROL, useKeyPress } from '@hooks/useKeyPress'; -import { usePokemonShortcutNavigation } from '@hooks/useShortcutNavigation'; type EvolutionDataBlockProps = { evolutionIndex: number; @@ -63,7 +63,10 @@ export const EvolutionDataBlock = ({ pokemonWithForm, evolutionIndex, setEvoluti disabled={evolution?.dbSymbol === '__undef__'} clickable={{ isClickable, - callback: () => shortcutNavigation(evolution?.dbSymbol || currentCreature.dbSymbol, evolution?.form), + callback: () => { + shortcutNavigation(evolution?.dbSymbol || currentCreature.dbSymbol, evolution?.form); + setEvolutionIndex(0); + }, }} /> {minLevel !== undefined && } diff --git a/src/views/components/editor/Editor.tsx b/src/views/components/editor/Editor.tsx index 26c110a05..7b48b7acb 100644 --- a/src/views/components/editor/Editor.tsx +++ b/src/views/components/editor/Editor.tsx @@ -79,7 +79,8 @@ type EditorProps = { | 'addition' | 'text' | 'reorganization' - | 'combo_moves'; + | 'combo_moves' + | 'assigning'; title: string; children: ReactNode; onClose?: () => void; diff --git a/src/views/components/home/ProjectCard.tsx b/src/views/components/home/ProjectCard.tsx index 76c12ce1f..83d5d7159 100644 --- a/src/views/components/home/ProjectCard.tsx +++ b/src/views/components/home/ProjectCard.tsx @@ -1,6 +1,6 @@ import { BaseIcon } from '@components/icons/BaseIcon'; import { ActiveContainer } from '@components/ActiveContainer'; -import React from 'react'; +import React, { useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import styled from 'styled-components'; import { useNavigate } from 'react-router-dom'; @@ -12,6 +12,7 @@ import { Project } from '@utils/projectList'; import { ResourceImage } from '@components/ResourceImage'; import { useShowItemInFolder } from '@hooks/useShowItemInFolder'; import { join } from '@utils/path'; +import { RmxpMigrationDialog } from './RmxpMigrationDialog'; const ProjectCardContainer = styled(ActiveContainer)` position: relative; @@ -103,6 +104,8 @@ export const ProjectCard = ({ project, onDeleteProjectToList, onUpdateProjectLis const projectLoad = useProjectLoad(); const navigate = useNavigate(); const showItemInFolder = useShowItemInFolder(); + const continueRef = useRef<(() => void) | null>(null); + const [showRmxpDialog, setShowRmxpDialog] = useState(false); const handleChangeFileClick = () => { return window.api.chooseProjectFileToOpen( @@ -133,7 +136,11 @@ export const ProjectCard = ({ project, onDeleteProjectToList, onUpdateProjectLis loaderRef.current.setError('loading_project_error', errorMessage); } }, - (count) => loaderRef.current.setError('loading_project_error', t('integrity_message', { count }), true) + (count) => loaderRef.current.setError('loading_project_error', t('integrity_message', { count }), true), + (onContinue) => { + continueRef.current = onContinue; + setShowRmxpDialog(true); + }, ); }; @@ -150,28 +157,38 @@ export const ProjectCard = ({ project, onDeleteProjectToList, onUpdateProjectLis ); }; - return project ? ( - - {project.projectStudio.iconPath ? ( - + return ( + <> + {showRmxpDialog && ( + continueRef.current?.()} + closeDialog={() => setShowRmxpDialog(false)} + /> + )} + {project ? ( + + {project.projectStudio.iconPath ? ( + + ) : ( + + )} +

{project.projectStudio.title}

+

+ {t('last_edit', { + date: project.lastEdit.toLocaleDateString(), + })} +

+ {`/${project.projectPath.replaceAll('\\', '/').split('/').splice(-1)[0]}`} + + +
) : ( - + )} -

{project.projectStudio.title}

-

- {t('last_edit', { - date: project.lastEdit.toLocaleDateString(), - })} -

- {`/${project.projectPath.replaceAll('\\', '/').split('/').splice(-1)[0]}`} - - -
- ) : ( - + ); }; diff --git a/src/views/components/home/RmxpMigrationDialog.tsx b/src/views/components/home/RmxpMigrationDialog.tsx new file mode 100644 index 000000000..9d0fb8ad3 --- /dev/null +++ b/src/views/components/home/RmxpMigrationDialog.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import styled from 'styled-components'; +import { EditorOverlayContainer } from '@components/editor'; +import { + MessageBoxActionContainer, + MessageBoxCancelLink, + MessageBoxContainer, + MessageBoxIconContainer, + MessageBoxTextContainer, + MessageBoxTitleIconContainer, +} from '@components/MessageBoxContainer'; +import { PrimaryButton, SecondaryButton } from '@components/buttons'; +import { BaseIcon } from '@components/icons/BaseIcon'; +import theme from '@src/AppTheme'; + +const RMXP_MIGRATION_GUIDE_URLS: Record = { + fr: 'https://pokemonworkshop.com/fr/learn/migrating-from-rpg-maker-xp-to-tiled', + en: 'https://pokemonworkshop.com/en/learn/migrating-from-rpg-maker-xp-to-tiled', +}; + +const getRmxpMigrationGuideUrl = (language: string) => RMXP_MIGRATION_GUIDE_URLS[language] ?? RMXP_MIGRATION_GUIDE_URLS['en']; + +const OverlayContainer = styled(EditorOverlayContainer)` + display: flex; + align-items: center; + justify-content: center; + z-index: 8001; +`; + +type RmxpMigrationDialogProps = { + onContinue: () => void; + closeDialog: () => void; +}; + +export const RmxpMigrationDialog = ({ onContinue, closeDialog }: RmxpMigrationDialogProps) => { + const { t, i18n } = useTranslation(); + + return ( + + + + + + +

{t('rmxp_migration_dialog_title')}

+
+ +

{t('rmxp_migration_dialog_text')}

+
+

{t('rmxp_migration_dialog_danger')}

+
+ + closeDialog()}> + {t('close')} + + window.api.externalWindow(getRmxpMigrationGuideUrl(i18n.language))}> + {t('rmxp_migration_dialog_learn_more')} + + { + closeDialog(); + onContinue(); + }} + > + {t('button_use_tiled')} + + +
+
+ ); +}; diff --git a/src/views/components/settings/editors/SettingsEditorOverlay.tsx b/src/views/components/settings/editors/SettingsEditorOverlay.tsx deleted file mode 100644 index 8840e9e6d..000000000 --- a/src/views/components/settings/editors/SettingsEditorOverlay.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; -import { defineEditorOverlay } from '@components/editor/EditorOverlayV2'; -import { assertUnreachable } from '@utils/assertUnreachable'; -import { DialogRefData } from '@hooks/useDialogsRef'; -import { SettingsMapsUseTiledMessageBox } from './SettingsMapsUseTiledMessageBox'; - -export type SettingsEditorAndDeletionKeys = 'use_tiled_message_box'; -export type SettingsDialogsRef = React.RefObject>; - -/** - * Editor overlay for the Studio settings. - * This component uses the generic editor overlay to show the components based on what's called from dialogsRef. - */ -export const SettingsEditorOverlay = defineEditorOverlay( - 'SettingsEditorOverlay', - (dialogToShow, handleCloseRef, closeDialog) => { - switch (dialogToShow) { - case 'use_tiled_message_box': - return ; - default: - return assertUnreachable(dialogToShow); - } - } -); diff --git a/src/views/components/settings/editors/SettingsMapsUseTiledMessageBox.tsx b/src/views/components/settings/editors/SettingsMapsUseTiledMessageBox.tsx deleted file mode 100644 index acf54b4f5..000000000 --- a/src/views/components/settings/editors/SettingsMapsUseTiledMessageBox.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import React, { forwardRef } from 'react'; -import { PrimaryButton } from '@components/buttons'; -import { useProjectStudio } from '@hooks/useProjectStudio'; -import { useTranslation } from 'react-i18next'; -import { - MessageBoxActionContainer, - MessageBoxCancelLink, - MessageBoxContainer, - MessageBoxIconContainer, - MessageBoxTextContainer, - MessageBoxTitleIconContainer, -} from '@components/MessageBoxContainer'; -import theme from '@src/AppTheme'; -import { BaseIcon } from '@components/icons/BaseIcon'; -import { EditorHandlingClose, useEditorHandlingClose } from '@components/editor/useHandleCloseEditor'; - -type SettingsMapsUseTiledMessageBoxProps = { - closeDialog: () => void; -}; - -export const SettingsMapsUseTiledMessageBox = forwardRef(({ closeDialog }, ref) => { - const { projectStudioValues: projectStudio, setProjectStudioValues: setProjectStudio } = useProjectStudio(); - const { t } = useTranslation(); - - const handleClick = () => { - setProjectStudio({ ...projectStudio, isTiledMode: true }); - closeDialog(); - }; - - useEditorHandlingClose(ref); - - return ( - - - - - -

{t('title_use_tiled_message_box')}

-
- -

{t('message_use_tiled_message_box')}

-

{t('important_use_tiled_message_box')}

-
- - {t('cancel')} - {t('button_use_tiled')} - -
- ); -}); -SettingsMapsUseTiledMessageBox.displayName = 'SettingsMapsUseTiledMessageBox'; diff --git a/src/views/components/world/WorldNavigation.tsx b/src/views/components/world/WorldNavigation.tsx index e19ab3bc1..f027e1a2d 100644 --- a/src/views/components/world/WorldNavigation.tsx +++ b/src/views/components/world/WorldNavigation.tsx @@ -4,7 +4,6 @@ import { useTranslation } from 'react-i18next'; import { NavigationDatabaseStyle } from '@components/database/navigation/NavigationDatabase/NavigationDatabaseStyle'; import { NavigationDatabaseItem } from '@components/database/navigation/NavigationDatabaseItem'; import { MapMenu } from './map'; -import { useProjectStudio } from '@root/src/hooks/useProjectStudio'; import { useLocation } from 'react-router-dom/dist'; import { EventMenu } from './event/EventMenu'; @@ -16,16 +15,10 @@ const WorldNavigationStyle = styled.div` min-width: 320px; `; -const WorlMapsEventDiv = styled.div<{ $showBorder?: boolean }>` +const WorlMapsEventDiv = styled.div` display: flex; flex-direction: row; gap: 8px; - ${({ $showBorder, theme }) => - $showBorder && - ` - border-bottom: 1px solid ${theme.colors.dark20}; - padding-bottom: 8px; - `} `; const WorldBuildingNavigationStyle = styled(NavigationDatabaseStyle)` @@ -34,16 +27,14 @@ const WorldBuildingNavigationStyle = styled(NavigationDatabaseStyle)` `; export const WorldNavigation = () => { - const { projectStudioValues } = useProjectStudio(); const { t } = useTranslation(); const location = useLocation(); - const isTiledMode = projectStudioValues.isTiledMode; const isMapPage = !!location.pathname.match(/^\/world\/(map|overview|maplink)/); const routeLinks = { events: '/world/events', map: '/world/map', - maplink: `/world/maplink${isTiledMode ? '2' : ''}`, + maplink: '/world/maplink2', }; const getMenuComponent = (pathname: string) => { @@ -61,13 +52,10 @@ export const WorldNavigation = () => { return ( - + - {(location.pathname === routeLinks.map || location.pathname === routeLinks.maplink) && !isTiledMode && ( - - )} {getMenuComponent(location.pathname)} diff --git a/src/views/components/world/event/treeEvent/EventTree.tsx b/src/views/components/world/event/treeEvent/EventTree.tsx index 84d1195f3..b2c1d923a 100644 --- a/src/views/components/world/event/treeEvent/EventTree.tsx +++ b/src/views/components/world/event/treeEvent/EventTree.tsx @@ -21,7 +21,7 @@ export const EventTree = () => { const treeScrollbarRef = useRef(null); return ( - + setResearch(event.target.value)} diff --git a/src/views/components/world/map/MapMenu.tsx b/src/views/components/world/map/MapMenu.tsx index fe4ca8334..8a221d7d1 100644 --- a/src/views/components/world/map/MapMenu.tsx +++ b/src/views/components/world/map/MapMenu.tsx @@ -44,7 +44,7 @@ const MapSubMenuContainer = styled.div` export const MapMenu = () => { const dialogsRef = useDialogsRef(); - const { mapInfo, isRMXPMode, setMapInfo } = useMapInfo(); + const { mapInfo, setMapInfo } = useMapInfo(); const { selectedDataIdentifier: currentMap } = useProjectMaps(); const { hasMapModified } = useMapPage(); const setText = useSetProjectText(); @@ -66,15 +66,11 @@ export const MapMenu = () => {
- dialogsRef.current?.openDialog('new')} disabled={isRMXPMode}> + dialogsRef.current?.openDialog('new')}> {t('new_map')} - - dialogsRef.current?.openDialog('full_update', true)} - data-tooltip={t('update_maps')} - disabled={isRMXPMode} - /> + + dialogsRef.current?.openDialog('full_update', true)} data-tooltip={t('update_maps')} />
diff --git a/src/views/components/world/map/MapRMXP2StudioUpdate.tsx b/src/views/components/world/map/MapRMXP2StudioUpdate.tsx deleted file mode 100644 index f6975a908..000000000 --- a/src/views/components/world/map/MapRMXP2StudioUpdate.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import { SecondaryButton } from '@components/buttons'; -import { DataBlockContainer } from '@components/database/dataBlocks'; -import { useRMXP2StudioMapsUpdate } from '@hooks/useRMXP2StudioMapsUpdate'; -import { showNotification } from '@utils/showNotification'; -import { useLoaderRef } from '@utils/loaderContext'; -import { MapUpdateContainer } from './MapUpdate'; - -const MapRMXPUpdateContainer = MapUpdateContainer; - -export const MapRMXP2StudioUpdate = () => { - const { t } = useTranslation(); - const update = useRMXP2StudioMapsUpdate(); - const loaderRef = useLoaderRef(); - - const handleUpdate = async () => { - update( - () => { - loaderRef.current.close(); - showNotification('success', t('update_rmxp_maps'), t('update_maps_success')); - }, - ({ errorMessage }) => { - loaderRef.current.setError('updating_maps_error', errorMessage, true); - } - ); - }; - - return ( - - -
-

{t('update_rmxp_maps')}

- {t('update_rmxp_maps_message')} -
- {t('update_rmxp_maps_button')} -
-
- ); -}; diff --git a/src/views/components/world/map/editors/MapImport/MapImport.tsx b/src/views/components/world/map/editors/MapImport/MapImport.tsx index 28c68a00f..80746d17a 100644 --- a/src/views/components/world/map/editors/MapImport/MapImport.tsx +++ b/src/views/components/world/map/editors/MapImport/MapImport.tsx @@ -1,18 +1,15 @@ -import React, { useEffect, useMemo, useState } from 'react'; +import { DarkButton, PrimaryButton } from '@components/buttons'; import { Dialog } from '@components/Dialog'; -import { useTranslation } from 'react-i18next'; import { DropInputFolder } from '@components/inputs'; -import { showNotification } from '@utils/showNotification'; -import { basename } from '@utils/path'; -import type { MapImportFiles } from './MapImportType'; -import { MapImportList } from './MapImportList'; -import { DarkButton, PrimaryButton } from '@components/buttons'; -import styled from 'styled-components'; import { useMapImport } from '@hooks/useMapImport'; import { useLoaderRef } from '@utils/loaderContext'; -import { DropDownOption } from '@components/StudioDropDown'; -import { useProjectDataReadonly } from '@hooks/useProjectData'; -import type { RMXPMapInfo } from '@hooks/useMapImport/types'; +import { basename } from '@utils/path'; +import { showNotification } from '@utils/showNotification'; +import React, { useEffect, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import styled from 'styled-components'; +import { MapImportList } from './MapImportList'; +import type { MapImportFiles } from './MapImportType'; const MapImportContainer = styled.div` display: flex; @@ -61,7 +58,7 @@ const defaultMapName = (filePath: string) => { return filename.replaceAll('_', ' '); }; -type MapImportState = 'select_folder' | 'searching_files' | 'load_rmxp_map_info' | 'select_files' | 'import'; +type MapImportState = 'select_folder' | 'searching_files' | 'select_files' | 'import'; type MapImportProps = { closeDialog: () => void; @@ -72,28 +69,23 @@ export const MapImport = ({ closeDialog, closeParentDialog }: MapImportProps) => const { t } = useTranslation(); const loaderRef = useLoaderRef(); const mapImport = useMapImport(); - const { projectDataValues: maps, state: globalState } = useProjectDataReadonly('maps', 'map'); const [state, setState] = useState('select_folder'); const [folderPath, setFolderPath] = useState(undefined); const [files, setFiles] = useState([]); const [hasError, setHasError] = useState(false); - const [mapInfoOptions, setMapInfoOptions] = useState([{ value: 'new', label: t('new_map') }]); - const [mapIdsUsed, setMapIdsUsed] = useState([]); - const [rmxpMapInfo, setRmxpMapInfo] = useState([]); const amountMapShouldBeImport = useMemo(() => files.filter((file) => file.shouldBeImport).length, [files]); const getSubTitle = () => { switch (state) { case 'select_folder': - return t('import_select_folder'); + return t('assign_select_folder'); case 'searching_files': - case 'load_rmxp_map_info': case 'select_files': case 'import': if (hasError) { - return t('import_error'); + return t('assign_error'); } - return t('import_select_maps'); + return t('assign_select_maps'); } }; @@ -115,46 +107,23 @@ export const MapImport = ({ closeDialog, closeParentDialog }: MapImportProps) => filename: basename(filePath), mapName: defaultMapName(filePath), shouldBeImport: false, - })) + })), ); - setState('load_rmxp_map_info'); + setState('select_files'); }, ({ errorMessage }) => { showNotification('danger', t('import_tiled_maps'), errorMessage); setFolderPath(undefined); setState('select_folder'); - } - ); - case 'load_rmxp_map_info': - return window.api.readRMXPMapInfo( - { projectPath: globalState.projectPath! }, - ({ rmxpMapInfo: mapInfo }) => { - const options = mapInfo.map(({ id, name }) => ({ value: id.toString(), label: `${name} (${id})` })); - const mapIds = Object.values(maps).map((map) => map.id); - setMapInfoOptions((mapInfoOptions) => { - mapInfoOptions.push(...options.filter((option) => option.value !== 'new' && !mapIds.includes(Number(option.value)))); - return mapInfoOptions; - }); - setRmxpMapInfo( - mapInfo.map(({ id, name }) => ({ - id, - name, - })) - ); - setState('select_files'); }, - ({ errorMessage }) => { - showNotification('warning', t('import_tiled_maps'), errorMessage); - setState('select_files'); - } ); case 'import': { const filesToImport = files.filter((file) => file.shouldBeImport); mapImport( - { filesToImport, tiledFilesSrcPath: folderPath!, rmxpMapInfo }, + { filesToImport, tiledFilesSrcPath: folderPath! }, () => { // we wait the end of the close dialog animation to show the result - setTimeout(() => loaderRef.current.setSuccess('importing_tiled_maps_success', t('import_success_message')), 200); + setTimeout(() => loaderRef.current.setSuccess('assigning_tiled_maps_success', t('import_success_message')), 200); closeDialog(); closeParentDialog(); }, @@ -165,7 +134,7 @@ export const MapImport = ({ closeDialog, closeParentDialog }: MapImportProps) => }); if (genericError) { // we wait the end of the close dialog animation to show the error - setTimeout(() => loaderRef.current.setError('importing_tiled_maps_error', genericError, true), 200); + setTimeout(() => loaderRef.current.setError('assigning_tiled_maps_error', genericError, true), 200); closeDialog(); closeParentDialog(); return; @@ -173,23 +142,15 @@ export const MapImport = ({ closeDialog, closeParentDialog }: MapImportProps) => loaderRef.current.close(); setHasError(true); setState('select_files'); - } + }, ); } } // eslint-disable-next-line react-hooks/exhaustive-deps }, [state]); - useEffect(() => { - if (state !== 'select_files') return; - const idsUsed = files.map(({ mapId }) => mapId).filter((mapId) => mapId !== undefined) as number[]; - setMapIdsUsed(idsUsed); - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [files]); - return ( - + {state === 'select_folder' && ( { @@ -203,17 +164,13 @@ export const MapImport = ({ closeDialog, closeParentDialog }: MapImportProps) => {(state === 'select_files' || state === 'searching_files' || state === 'import') && ( {state === 'select_files' && files.length === 0 &&
{t('no_files_found')}
} - {((state === 'select_files' && files.length > 0) || state === 'import') && ( - - )} + {((state === 'select_files' && files.length > 0) || state === 'import') && } {state === 'searching_files' &&
{t('searching_files')}
}
{ setFolderPath(undefined); setFiles([]); - setMapInfoOptions([{ value: 'new', label: t('new_map') }]); - setRmxpMapInfo([]); setState('select_folder'); setHasError(false); }} @@ -224,12 +181,12 @@ export const MapImport = ({ closeDialog, closeParentDialog }: MapImportProps) => {t('cancel')} - {amountMapShouldBeImport === 0 && {t('import')}} + {amountMapShouldBeImport === 0 && {t('assign')}} {amountMapShouldBeImport > 0 && ( {amountMapShouldBeImport === 1 - ? t('import_selected_map_singular') - : t('import_selected_map_plural', { amount: amountMapShouldBeImport })} + ? t('assign_selected_map_singular') + : t('assign_selected_map_plural', { amount: amountMapShouldBeImport })} )}
diff --git a/src/views/components/world/map/editors/MapImport/MapImportList.tsx b/src/views/components/world/map/editors/MapImport/MapImportList.tsx index e9f7be14b..b8dfdbece 100644 --- a/src/views/components/world/map/editors/MapImport/MapImportList.tsx +++ b/src/views/components/world/map/editors/MapImport/MapImportList.tsx @@ -1,13 +1,15 @@ -import React, { Dispatch, SetStateAction } from 'react'; -import { MapImportFiles } from './MapImportType'; -import styled from 'styled-components'; -import { useTranslation } from 'react-i18next'; +import ErrorIcon from '@assets/icons/global/error2.svg'; import { Checkbox } from '@components/Checkbox'; -import { AutoSizer, List } from 'react-virtualized'; import { Input } from '@components/inputs'; +import { Select } from '@ds/Select'; +import { SelectContainer } from '@ds/Select/SelectContainer'; +import { useSelectOptions } from '@hooks/useSelectOptions'; import { cloneEntity } from '@utils/cloneEntity'; -import ErrorIcon from '@assets/icons/global/error2.svg'; -import { DropDownOption, StudioDropDown } from '@components/StudioDropDown'; +import React, { Dispatch, SetStateAction, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { AutoSizer, List } from 'react-virtualized'; +import styled from 'styled-components'; +import { MapImportFiles } from './MapImportType'; const MapImportListContainer = styled.div` ${({ theme }) => theme.fonts.normalRegular} @@ -67,7 +69,15 @@ const MapImportListContainer = styled.div` margin-left: -8px; margin-right: -12px; + ${SelectContainer} { + width: 240px; + } + & .scrollable-view { + & .ReactVirtualized__Grid__innerScrollContainer { + overflow: visible !important; // fix the overflow to show the select options + } + ::-webkit-scrollbar { width: 8px; height: 8px; @@ -123,13 +133,14 @@ const MapLineContainer = styled.div` type MapImportListType = { files: MapImportFiles[]; - mapInfoOptions: DropDownOption[]; - mapIdsUsed: number[]; setFiles: Dispatch>; }; -export const MapImportList = ({ files, mapInfoOptions, mapIdsUsed, setFiles }: MapImportListType) => { +export const MapImportList = ({ files, setFiles }: MapImportListType) => { const { t } = useTranslation(); + const mapOptions = useSelectOptions('maps'); + // eslint-disable-next-line react-hooks/exhaustive-deps + const options = useMemo(() => [{ value: 'new', label: t('new_map') }, ...mapOptions], [mapOptions]); const allFilesChecked = (checked: boolean) => { setFiles(files.map((file) => ({ ...file, shouldBeImport: (file.shouldBeImport = checked) }))); @@ -147,21 +158,21 @@ export const MapImportList = ({ files, mapInfoOptions, mapIdsUsed, setFiles }: M setFiles(filesCloned); }; - const handleMapId = (value: string, index: number) => { + const handleMapId = (dbSymbol: string, index: number) => { const filesCloned = cloneEntity(files); - filesCloned[index].mapId = value === 'new' ? undefined : Number(value); + filesCloned[index].dbSymbol = dbSymbol === 'new' ? undefined : dbSymbol; setFiles(filesCloned); }; return (
-
+
!file.shouldBeImport)} onChange={(event) => allFilesChecked(event.target.checked)} /> {t('file')}
+ {t('map_in_studio')} {t('map_name')} - {t('map_in_rmxp')}
@@ -178,7 +189,7 @@ export const MapImportList = ({ files, mapInfoOptions, mapIdsUsed, setFiles }: M const hasError = file.error !== undefined; return ( -
+
handleFileChecked(event.target.checked, index)} />
{file.filename} @@ -190,11 +201,10 @@ export const MapImportList = ({ files, mapInfoOptions, mapIdsUsed, setFiles }: M
handleMapName(event.target.value, index)} /> - handleMapId(value, index)} - optionals={{ filter: (value) => file.mapId === Number(value) || !mapIdsUsed.includes(Number(value)) }} /> ); diff --git a/src/views/components/world/map/editors/MapImport/MapImportType.ts b/src/views/components/world/map/editors/MapImport/MapImportType.ts index 67533cd66..e460e974e 100644 --- a/src/views/components/world/map/editors/MapImport/MapImportType.ts +++ b/src/views/components/world/map/editors/MapImport/MapImportType.ts @@ -3,6 +3,6 @@ export type MapImportFiles = { filename: string; mapName: string; shouldBeImport: boolean; - mapId?: number; + dbSymbol?: string; error?: string; }; diff --git a/src/views/components/world/map/editors/MapNewEditor.tsx b/src/views/components/world/map/editors/MapNewEditor.tsx index 068de2b77..e4e380870 100644 --- a/src/views/components/world/map/editors/MapNewEditor.tsx +++ b/src/views/components/world/map/editors/MapNewEditor.tsx @@ -1,6 +1,7 @@ -import React, { forwardRef, useRef, useState } from 'react'; -import { useTranslation } from 'react-i18next'; +import { DarkButton, PrimaryButton, SecondaryButton } from '@components/buttons'; import { Editor, EditorWithCollapse } from '@components/editor'; +import { EditorChildWithSubEditorContainer, SubEditorContainer, SubEditorSeparator } from '@components/editor/EditorContainer'; +import { EditorHandlingClose, useEditorHandlingClose } from '@components/editor/useHandleCloseEditor'; import { FileInput, Input, @@ -11,31 +12,30 @@ import { MultiLineInput, PaddedInputContainer, } from '@components/inputs'; +import { AUDIO_EXT } from '@components/inputs/AudioInput'; +import { DropInput } from '@components/inputs/DropInput'; +import { TextInputError } from '@components/inputs/Input'; +import { InputGroupCollapse } from '@components/inputs/InputContainerCollapse'; +import { TooltipWrapper } from '@ds/Tooltip'; +import { useDialogsRef } from '@hooks/useDialogsRef'; +import { useMapCopy } from '@hooks/useMapCopy'; +import { useMapInfo } from '@hooks/useMapInfo'; import { useProjectMapLinks, useProjectMaps } from '@hooks/useProjectData'; -import styled from 'styled-components'; -import { DarkButton, PrimaryButton, SecondaryButton } from '@components/buttons'; import { MAP_DESCRIPTION_TEXT_ID, MAP_NAME_TEXT_ID } from '@modelEntities/map'; +import { StudioMapInfoMap, StudioMapInfoValue } from '@modelEntities/mapInfo'; +import { cloneEntity } from '@utils/cloneEntity'; import { createMap, createMapInfo } from '@utils/entityCreation'; -import { useSetProjectText } from '@utils/ReadingProjectText'; -import { EditorHandlingClose, useEditorHandlingClose } from '@components/editor/useHandleCloseEditor'; -import { InputGroupCollapse } from '@components/inputs/InputContainerCollapse'; -import { DropInput } from '@components/inputs/DropInput'; +import { useLoaderRef } from '@utils/loaderContext'; +import { addNewMapInfo, mapInfoNewMapWithParent } from '@utils/MapInfoUtils'; +import { createMapLinkFromMainMapId } from '@utils/MapLinkUtils'; import { basename } from '@utils/path'; +import { useSetProjectText } from '@utils/ReadingProjectText'; +import React, { forwardRef, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; -import { AUDIO_EXT } from '@components/inputs/AudioInput'; -import { cloneEntity } from '@utils/cloneEntity'; -import { useMapInfo } from '@hooks/useMapInfo'; -import { StudioMapInfoMap, StudioMapInfoValue } from '@modelEntities/mapInfo'; -import { addNewMapInfo, mapInfoNewMapWithParent } from '@utils/MapInfoUtils'; -import { EditorChildWithSubEditorContainer, SubEditorContainer, SubEditorSeparator } from '@components/editor/EditorContainer'; +import styled from 'styled-components'; import { MapImportEditorTitle, MapImportOverlay } from './MapImport/MapImportOverlay'; -import { useDialogsRef } from '@hooks/useDialogsRef'; import { useUpdateMapModified } from './useUpdateMapModified'; -import { useMapCopy } from '@hooks/useMapCopy'; -import { useLoaderRef } from '@utils/loaderContext'; -import { TextInputError } from '@components/inputs/Input'; -import { TooltipWrapper } from '@ds/Tooltip'; -import { createMapLinkFromMainMapId } from '@utils/MapLinkUtils'; const ButtonContainer = styled.div` display: flex; @@ -124,7 +124,7 @@ export const MapNewEditor = forwardRef(( (genericError) => { setTimeout(() => loaderRef.current.setError('importing_tiled_maps_error', genericError, true), 200); closeDialog(); - } + }, ); }; @@ -229,8 +229,8 @@ export const MapNewEditor = forwardRef(( - - dialogsRef.current?.openDialog('import', true)}>{t('import')} + + dialogsRef.current?.openDialog('import', true)}>{t('assign')} diff --git a/src/views/components/world/map/index.tsx b/src/views/components/world/map/index.tsx index 6a04875d3..27ef13308 100644 --- a/src/views/components/world/map/index.tsx +++ b/src/views/components/world/map/index.tsx @@ -3,5 +3,4 @@ export { MapMusics } from './MapMusics'; export { MapMenu } from './MapMenu'; export { MapUpdate } from './MapUpdate'; export { MapBreadcrumb } from './MapBreadcrumb'; -export { MapRMXP2StudioUpdate } from './MapRMXP2StudioUpdate'; export { MapEmptyState } from './MapEmptyState'; diff --git a/src/views/components/world/map/tree/MapTree.tsx b/src/views/components/world/map/tree/MapTree.tsx index 9389f0848..543db2096 100644 --- a/src/views/components/world/map/tree/MapTree.tsx +++ b/src/views/components/world/map/tree/MapTree.tsx @@ -5,16 +5,14 @@ import { MapList } from './MapList'; import { MapTreeComponent } from './MapTreeComponent'; import { emitScrollContextMenu } from '@hooks/useContextMenu'; import { MapTreeContainer } from './style'; -import { useProjectStudio } from '@hooks/useProjectStudio'; export const MapTree = () => { - const { projectStudioValues } = useProjectStudio(); const [research, setResearch] = useState(''); const { t } = useTranslation(); const treeScrollbarRef = useRef(null); return ( - + setResearch(event.target.value)} diff --git a/src/views/components/world/map/tree/MapTreeComponent.tsx b/src/views/components/world/map/tree/MapTreeComponent.tsx index 96eee0a30..07a5bdfed 100644 --- a/src/views/components/world/map/tree/MapTreeComponent.tsx +++ b/src/views/components/world/map/tree/MapTreeComponent.tsx @@ -45,7 +45,7 @@ type MapTreeComponentProps = { }; export const MapTreeComponent = ({ treeScrollbarRef }: MapTreeComponentProps) => { - const { mapInfo, isRMXPMode, setMapInfo, setPartialMapInfo } = useMapInfo(); + const { mapInfo, setMapInfo, setPartialMapInfo } = useMapInfo(); const { selectedDataIdentifier: currentMap, setSelectedDataIdentifier: setCurrentMap, projectDataValues: maps } = useProjectMaps(); const setText = useSetProjectText(); const getMapName = useGetEntityNameText(); @@ -173,8 +173,6 @@ export const MapTreeComponent = ({ treeScrollbarRef }: MapTreeComponentProps) => const openMenu = (event: React.MouseEvent) => { event.preventDefault(); event.stopPropagation(); - if (isRMXPMode) return; - setMapInfoSelected(mapInfo[item.id]); // timeout to wait that the mapinfo selected has been taken into account setTimeout(() => buildOnClick(event, true)); @@ -223,12 +221,10 @@ export const MapTreeComponent = ({ treeScrollbarRef }: MapTreeComponentProps) => {isFolder && !!countChildren && {countChildren}} {!canRename && (
- {!isRMXPMode && ( - - - - )} - {!isDeleted && !isRMXPMode && currentDepth <= 3 && ( + + + + {!isDeleted && currentDepth <= 3 && ( { @@ -295,7 +291,7 @@ export const MapTreeComponent = ({ treeScrollbarRef }: MapTreeComponentProps) => onCollapse={onCollapse} onDragEnd={onDragEnd} offsetPerLevel={26} - isDragEnabled={!isRMXPMode} + isDragEnabled={true} isNestingEnabled /> )} diff --git a/src/views/components/world/map/tree/style/TreeStyle.tsx b/src/views/components/world/map/tree/style/TreeStyle.tsx index 9c9aa44f8..c686f8554 100644 --- a/src/views/components/world/map/tree/style/TreeStyle.tsx +++ b/src/views/components/world/map/tree/style/TreeStyle.tsx @@ -2,7 +2,6 @@ import styled from 'styled-components'; type TreeContainerProps = { hideMapTree: boolean; - isTiledMode: boolean; }; export const TreeContainer = styled.div` @@ -13,7 +12,7 @@ export const TreeContainer = styled.div` .tree-scrollbar { overflow-y: scroll; margin-right: -9px; - height: calc(100vh - ${({ isTiledMode }) => (isTiledMode ? '253px' : '313px')}); + height: calc(100vh - 253px); display: ${({ hideMapTree }) => (hideMapTree ? 'none' : 'block')}; ::-webkit-scrollbar { diff --git a/src/views/pages/dashboard/Dashboard.page.tsx b/src/views/pages/dashboard/Dashboard.page.tsx index cd2e7911e..abd4a2167 100644 --- a/src/views/pages/dashboard/Dashboard.page.tsx +++ b/src/views/pages/dashboard/Dashboard.page.tsx @@ -1,14 +1,13 @@ import { SecondaryButton } from '@components/buttons'; import { DataBlockWithAction, DataBlockWrapper } from '@components/database/dataBlocks'; import { PageContainerStyle, PageDataConstrainerStyle } from '@pages/database/PageContainerStyle'; -import React, { useEffect } from 'react'; +import React from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import styled from 'styled-components'; import { DashboardPageStyle } from './DashboardPageStyle'; import { useDialogsRef } from '@hooks/useDialogsRef'; import { DashboardEditorAndDeletionKeys, DashboardEditorOverlay } from '@components/dashboard/editors/DashboardEditorOverlay'; -import { useProjectStudio } from '@hooks/useProjectStudio'; import { Onboarding } from '@components/onboarding/Onboarding'; import { DashboardControlBar, DashboardFrame } from '@components/dashboard'; @@ -23,13 +22,6 @@ export const DashboardPage = () => { const { t } = useTranslation(); const navigate = useNavigate(); const dialogsRef = useDialogsRef(); - const { projectStudioValues: projectStudio } = useProjectStudio(); - - useEffect(() => { - if (projectStudio.isTiledMode !== null) return; - - if (!dialogsRef.current?.currentDialog) dialogsRef.current?.openDialog('studio_mode_message_box', true); - }, [dialogsRef, projectStudio]); return ( diff --git a/src/views/pages/settings/Settings.maps.page.tsx b/src/views/pages/settings/Settings.maps.page.tsx index 06dcbf6fd..e79e5c246 100644 --- a/src/views/pages/settings/Settings.maps.page.tsx +++ b/src/views/pages/settings/Settings.maps.page.tsx @@ -1,14 +1,11 @@ import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { PageEditor, PageTemplate } from '@components/pages'; -import { InputWithLeftLabelContainer, Label, Toggle, InputWithTopLabelContainer, DropInput, FileInput } from '@components/inputs'; -import { useProjectStudio } from '@hooks/useProjectStudio'; +import { InputWithTopLabelContainer, Label, DropInput, FileInput } from '@components/inputs'; import styled from 'styled-components'; import { Link } from '@components/Link'; import LinkStyle from '@components/Link/LinkStyle'; import { getSetting, updateSettings } from '@utils/settings'; -import { SettingsEditorAndDeletionKeys, SettingsEditorOverlay } from '@components/settings/editors/SettingsEditorOverlay'; -import { useDialogsRef } from '@hooks/useDialogsRef'; import { basename } from '@utils/path'; import { showNotification } from '@utils/showNotification'; @@ -24,9 +21,7 @@ const DownloadMessageContainer = styled.div` `; export const SettingsMapsPage = () => { - const { projectStudioValues: projectStudio } = useProjectStudio(); const [tiledPath, setTiledPath] = useState(getSetting('tiledPath')); - const dialogsRef = useDialogsRef(); const { t } = useTranslation(); const isWin32 = window.api.platform === 'win32'; @@ -48,42 +43,27 @@ export const SettingsMapsPage = () => { return ( - - - { - event.preventDefault(); - dialogsRef.current?.openDialog('use_tiled_message_box', true); - }} - disabled={projectStudio.isTiledMode || false} - /> - - {projectStudio.isTiledMode && ( - - - {tiledPath ? ( - - ) : ( - - )} - - {t('download_message')} - - - - )} - + + + {tiledPath ? ( + + ) : ( + + )} + + {t('download_message')} + + + ); diff --git a/src/views/pages/world/Map.page.tsx b/src/views/pages/world/Map.page.tsx index 21ac656de..63ca46e4d 100644 --- a/src/views/pages/world/Map.page.tsx +++ b/src/views/pages/world/Map.page.tsx @@ -9,7 +9,7 @@ import { useDialogsRef } from '@hooks/useDialogsRef'; import { useMapPage } from '@hooks/usePage'; import { MapEditorOverlay } from '@components/world/map/editors'; import { MapEditorAndDeletionKeys } from '@components/world/map/editors/MapEditorOverlay'; -import { MapBreadcrumb, MapEmptyState, MapFrame, MapMusics, MapRMXP2StudioUpdate } from '@components/world/map'; +import { MapBreadcrumb, MapEmptyState, MapFrame, MapMusics } from '@components/world/map'; import { DeleteButtonWithIcon, SecondaryButton } from '@components/buttons'; import { BaseIcon } from '@components/icons/BaseIcon'; import theme from '@src/AppTheme'; @@ -31,7 +31,7 @@ export const MapPageStyle = styled.div` export const MapPage = () => { const dialogsRef = useDialogsRef(); const dialogsMapImportRef = useDialogsRef(); - const { map, hasMap, isRMXPMode, disabledOpenTiled } = useMapPage(); + const { map, hasMap, disabledOpenTiled } = useMapPage(); const openTiled = useOpenTiled(); const navigateMapLink = useNavigateMapLink(); const { t } = useTranslation(); @@ -50,11 +50,10 @@ export const MapPage = () => { { label: t('map'), path: '/world/overview', disabled: disabledOpenTiled }, ]} /> - {isRMXPMode && } - - + + @@ -71,16 +70,14 @@ export const MapPage = () => { - - dialogsRef.current?.openDialog('deletion', true)} disabled={isRMXPMode}> - {t('delete_this_map')} - + + dialogsRef.current?.openDialog('deletion', true)}>{t('delete_this_map')} ) : ( - !isRMXPMode && + )} {}} />