From cbfb30ee209c626c2e5cc0cbeb8ab98d00fa516c Mon Sep 17 00:00:00 2001 From: Kyle Travis Date: Fri, 16 May 2025 05:56:22 -0500 Subject: [PATCH] first commit --- README.md | 1 + module.json | 13 +++ shadowdarklings-importer.js | 155 ++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 README.md create mode 100644 module.json create mode 100644 shadowdarklings-importer.js diff --git a/README.md b/README.md new file mode 100644 index 0000000..0949beb --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +First Iteration diff --git a/module.json b/module.json new file mode 100644 index 0000000..40cc111 --- /dev/null +++ b/module.json @@ -0,0 +1,13 @@ +{ + "name": "shadowdarklings-importer", + "title": "Shadowdarklings.net Importer", + "description": "Import JSON characters and items from shadowdarklings.net into Foundry VTT.", + "version": "1.0.0", + "author": "Booga", + "minimumCoreVersion": "10", + "compatibleCoreVersion": "11", + "esmodules": ["shadowdarklings-importer.js"], + "systems": ["shadowdark"], + "manifest": "https://yourdomain.com/path/to/module.json", + "download": "https://yourdomain.com/path/to/module.zip" +} diff --git a/shadowdarklings-importer.js b/shadowdarklings-importer.js new file mode 100644 index 0000000..a57f0f4 --- /dev/null +++ b/shadowdarklings-importer.js @@ -0,0 +1,155 @@ +// 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."); +}