Import des persos du système précédent

This commit is contained in:
2026-05-06 21:31:03 +02:00
parent fbfb265570
commit 73a3381d2a
14 changed files with 3296 additions and 3 deletions
+148
View File
@@ -0,0 +1,148 @@
/**
* Chroniques de l'Étrange — Système FoundryVTT
*
* Chroniques de l'Étrange est un jeu de rôle édité par Antre-Monde Éditions.
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
* affilié à Antre-Monde Éditions,
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
*
* @author LeRatierBretonnien
* @copyright 20242026 LeRatierBretonnien
* @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
*/
import { parseLegacyJson } from "../../migration/migrator.js"
const MIGRATION_TEMPLATE = "systems/fvtt-chroniques-de-l-etrange/templates/apps/cde-migration-app.html"
/**
* Dialog for importing legacy CDE actor JSON files into the current system.
*
* Accessible via the System Settings menu (registerMenu).
* Supports multi-file selection and shows a preview table before importing.
*/
export class CDEMigrationApp extends foundry.applications.api.HandlebarsApplicationMixin(
foundry.applications.api.ApplicationV2
) {
static DEFAULT_OPTIONS = {
id: "cde-migration-app",
classes: ["cde-migration-app"],
tag: "div",
window: {
title: "CDE.MigrationTitle",
icon: "fas fa-file-import",
resizable: false,
},
position: { width: 560, height: "auto" },
actions: {
clearFiles: CDEMigrationApp.#clearFiles,
doImport: CDEMigrationApp.#doImport,
},
}
static PARTS = {
form: { template: MIGRATION_TEMPLATE },
}
/** @type {Array<{name: string, type: string, img: string, system: object, items: object[], _srcFile: string}>} */
#pending = []
/** @type {string[]} - error messages per file */
#errors = []
async _prepareContext(options) {
return {
pending: this.#pending,
errors: this.#errors,
hasPending: this.#pending.length > 0,
hasErrors: this.#errors.length > 0,
count: this.#pending.length,
}
}
/** After render, wire up the file input. */
_onRender(context, options) {
super._onRender(context, options)
const input = this.element.querySelector(".cde-migration-file-input")
input?.addEventListener("change", this.#onFileChange.bind(this))
const dropZone = this.element.querySelector(".cde-migration-drop-zone")
if (dropZone) {
dropZone.addEventListener("dragover", (e) => { e.preventDefault(); dropZone.classList.add("is-dragover") })
dropZone.addEventListener("dragleave", () => dropZone.classList.remove("is-dragover"))
dropZone.addEventListener("drop", (e) => {
e.preventDefault()
dropZone.classList.remove("is-dragover")
this.#processFiles(Array.from(e.dataTransfer.files))
})
}
}
async #onFileChange(event) {
const files = Array.from(event.target.files ?? [])
event.target.value = ""
await this.#processFiles(files)
}
async #processFiles(files) {
for (const file of files) {
if (!file.name.endsWith(".json")) {
this.#errors.push(game.i18n.format("CDE.MigrationErrorNotJson", { file: file.name }))
continue
}
try {
const text = await file.text()
const actors = parseLegacyJson(text)
for (const actor of actors) {
actor._srcFile = file.name
// Avoid duplicates by name
if (!this.#pending.some(p => p.name === actor.name)) {
this.#pending.push(actor)
}
}
} catch (err) {
this.#errors.push(game.i18n.format("CDE.MigrationErrorParse", { file: file.name, error: err.message }))
}
}
this.render()
}
static async #clearFiles() {
this.#pending = []
this.#errors = []
this.render()
}
static async #doImport() {
if (!this.#pending.length) return
const created = []
const failed = []
for (const data of this.#pending) {
try {
const { _srcFile, ...actorData } = data
const actor = await Actor.create(actorData)
created.push(actor.name)
} catch (err) {
failed.push(`${data.name}: ${err.message}`)
console.error(`CHRONIQUESDELETRANGE | Migration failed for "${data.name}":`, err)
}
}
this.#pending = []
this.#errors = failed
this.render()
if (created.length) {
ui.notifications.info(
game.i18n.format("CDE.MigrationSuccess", { count: created.length, names: created.join(", ") })
)
}
if (failed.length) {
ui.notifications.warn(
game.i18n.format("CDE.MigrationPartialError", { count: failed.length })
)
}
}
}