// shadowdarklings-importer.js Hooks.on("ready", function () { // Add button to Actors Directory Hooks.on("renderActorDirectory", (app, html, data) => { appendImportButton(html, app, "actor"); }); // Add button to Items Directory Hooks.on("renderItemDirectory", (app, html, data) => { appendImportButton(html, app, "item"); }); }); function appendImportButton(html, app, type) { const importBtn = $( `` ); importBtn.on("click", async () => { const dialog = new ShadowdarklingsImportDialog(type); dialog.render(true); }); html.find(".directory-footer").append(importBtn); } // A dialog window to upload JSON and start the import process class ShadowdarklingsImportDialog extends Dialog { constructor(type) { super({ title: `Import Shadowdarklings ${type === "actor" ? "Character/Monster" : "Item"}`, content: `
`, buttons: { import: { icon: '', label: "Import", callback: (html) => this.importJSON(html) }, cancel: { icon: '', label: "Cancel" } }, default: "import" }); this.type = type; } async importJSON(html) { const file = html.find('input[name="json"]')[0]?.files[0]; if (!file) { ui.notifications.error("No file selected!"); return; } let json; try { json = JSON.parse(await file.text()); } catch (err) { ui.notifications.error("Invalid JSON file."); return; } // Dispatch to appropriate handler if (this.type === "actor") { return importShadowdarklingsActor(json); } else { return importShadowdarklingsItem(json); } } } // Example mapping function for actor async function importShadowdarklingsActor(data) { // Try to discern if this is a character or monster format if (!data.name) { ui.notifications.error("This JSON does not look like a Shadowdark character."); return; } // Very basic mapping, adjust as needed const actorData = { name: data.name || "Imported Character", type: data.class ? "character" : "npc", // Guess by "class" being present system: { abilities: { str: { value: data.str }, dex: { value: data.dex }, int: { value: data.int }, wis: { value: data.wis }, con: { value: data.con }, cha: { value: data.cha } }, ac: { value: data.ac }, hp: { value: data.hp, max: data.hp }, level: { value: data.level }, class: { value: data.class }, race: { value: data.race }, alignment: { value: data.alignment }, // Add more mappings as fits your needs and data }, // Optionally add avatar/portrait: img: data.img // Optionally import inventory as embedded items, etc. }; // Try to import inventory if present if (Array.isArray(data.inventory)) { actorData.items = data.inventory.map(itm => ({ name: itm.name, type: "equipment", // Guess, or use your actual item types system: { description: { value: itm.description || "" }, quantity: itm.quantity || 1, value: itm.value || "", weight: itm.weight || "", } })); } // Create the actor in the current folder try { await Actor.create(actorData); ui.notifications.info(`${actorData.name} imported successfully!`); } catch (e) { console.error(e); ui.notifications.error("Actor import failed."); } } async function importShadowdarklingsItem(data) { // Accept single or array of items const items = Array.isArray(data) ? data : [data]; for (let itm of items) { if (!itm.name) continue; const itemData = { name: itm.name, type: itm.type || "equipment", system: { description: { value: itm.description || "" }, // Add other fields as needed }, }; try { await Item.create(itemData); } catch (e) { console.error(e); ui.notifications.warn(`Failed to import item: ${itm.name}`); } } ui.notifications.info("Items imported."); }