2 Commits
v1.0.3 ... main

Author SHA1 Message Date
35078e9e26 updated with correct module 2025-05-16 15:24:17 -05:00
24521f593f updated js for v12 2025-05-16 06:21:19 -05:00
2 changed files with 49 additions and 38 deletions

View File

@@ -2,12 +2,12 @@
"name": "shadowdarklings-importer", "name": "shadowdarklings-importer",
"title": "Shadowdarklings.net Importer", "title": "Shadowdarklings.net Importer",
"description": "Import JSON characters and items from shadowdarklings.net into Foundry VTT.", "description": "Import JSON characters and items from shadowdarklings.net into Foundry VTT.",
"version": "1.0.3", "version": "1.0.5",
"author": "Booga", "author": "Booga",
"minimumCoreVersion": "11", "minimumCoreVersion": "11",
"compatibleCoreVersion": "12", "compatibleCoreVersion": "12",
"esmodules": ["shadowdarklings-importer.js"], "esmodules": ["shadowdarklings-importer.js"],
"systems": ["shadowdark"], "systems": ["shadowdark"],
"manifest": "https://g.kht.dev/kyle/shadowdark-importer/module.json", "manifest": "https://g.kht.dev/kyle/shadowdark-importer/raw/branch/main/module.json",
"download": "https://g.kht.dev/kyle/shadowdark-importer/archive/v1.0.3.zip" "download": "https://g.kht.dev/kyle/shadowdark-importer/archive/v1.0.5.zip"
} }

View File

@@ -1,5 +1,5 @@
// shadowdarklings-importer.js // shadowdarklings-importer.js
Hooks.on("ready", function () { Hooks.once("ready", function () {
// Add button to Actors Directory // Add button to Actors Directory
Hooks.on("renderActorDirectory", (app, html, data) => { Hooks.on("renderActorDirectory", (app, html, data) => {
appendImportButton(html, app, "actor"); appendImportButton(html, app, "actor");
@@ -15,16 +15,15 @@ function appendImportButton(html, app, type) {
`<button class="shadowdarklings-importer"><i class="fas fa-file-import"></i> Shadowdarklings Import</button>` `<button class="shadowdarklings-importer"><i class="fas fa-file-import"></i> Shadowdarklings Import</button>`
); );
importBtn.on("click", async () => { importBtn.on("click", async () => {
const dialog = new ShadowdarklingsImportDialog(type); const dialog = new ShadowdarklingsImportDialog(type, app);
dialog.render(true); dialog.render(true);
}); });
html.find(".directory-footer").append(importBtn); html.find(".directory-footer").append(importBtn);
} }
// A dialog window to upload JSON and start the import process // Upload dialog
class ShadowdarklingsImportDialog extends Dialog { class ShadowdarklingsImportDialog extends Dialog {
constructor(type) { constructor(type, app) {
super({ super({
title: `Import Shadowdarklings ${type === "actor" ? "Character/Monster" : "Item"}`, title: `Import Shadowdarklings ${type === "actor" ? "Character/Monster" : "Item"}`,
content: ` content: `
@@ -49,6 +48,7 @@ class ShadowdarklingsImportDialog extends Dialog {
default: "import" default: "import"
}); });
this.type = type; this.type = type;
this.app = app;
} }
async importJSON(html) { async importJSON(html) {
@@ -65,27 +65,30 @@ class ShadowdarklingsImportDialog extends Dialog {
return; return;
} }
// Dispatch to appropriate handler
if (this.type === "actor") { if (this.type === "actor") {
return importShadowdarklingsActor(json); return importShadowdarklingsActor(json, this.app);
} else { } else {
return importShadowdarklingsItem(json); return importShadowdarklingsItem(json, this.app);
} }
} }
} }
// Example mapping function for actor // Main actor importer
async function importShadowdarklingsActor(data) { async function importShadowdarklingsActor(data, app) {
// Try to discern if this is a character or monster format // Use system-defined actor types: 'pc' (player), 'npc' (monsters/opponents)
if (!data.name) { // Try to discern if this is a PC or NPC/Monster.
ui.notifications.error("This JSON does not look like a Shadowdark character."); let actorType = "npc";
return; // If class or level is present, it's likely a PC
} if (data.class || data.level || data.background || data.player) actorType = "pc";
// Very basic mapping, adjust as needed // Allowed actor types
const allowedTypes = game.system.model.Actor ? Object.keys(game.system.model.Actor) : ["pc","npc"];
if (!allowedTypes.includes(actorType)) actorType = allowedTypes[0]; // Fallback
// Setup the document data
const actorData = { const actorData = {
name: data.name || "Imported Character", name: data.name || "Imported Character",
type: data.class ? "character" : "npc", // Guess by "class" being present type: actorType,
system: { system: {
abilities: { abilities: {
str: { value: data.str }, str: { value: data.str },
@@ -96,60 +99,68 @@ async function importShadowdarklingsActor(data) {
cha: { value: data.cha } cha: { value: data.cha }
}, },
ac: { value: data.ac }, ac: { value: data.ac },
hp: { value: data.hp, max: data.hp }, hp: { value: data.hp, max: data.hp || data.maxHp, min: 0 },
level: { value: data.level }, level: { value: data.level },
class: { value: data.class }, class: { value: data.class },
race: { value: data.race }, ancestry: { value: data.race },
alignment: { value: data.alignment }, alignment: { value: data.alignment }
// Add more mappings as fits your needs and data // Extend as needed for your Shadowdark system version
}, },
// Optionally add avatar/portrait: img: data.img // Optionally add image
// Optionally import inventory as embedded items, etc. // img: data.img || "icons/svg/mystery-man.svg"
}; };
// Try to import inventory if present // Try inventory import (if system supports embedded items in actors)
if (Array.isArray(data.inventory)) { if (Array.isArray(data.inventory)) {
actorData.items = data.inventory.map(itm => ({ actorData.items = data.inventory.filter(i=>i.name).map(itm => ({
name: itm.name, name: itm.name,
type: "equipment", // Guess, or use your actual item types type: "equipment", // Or "gear" or your actual item type in Shadowdark
system: { system: {
description: { value: itm.description || "" }, description: { value: itm.description || "" },
quantity: itm.quantity || 1, quantity: itm.quantity || 1,
value: itm.value || "", value: itm.value || "",
weight: itm.weight || "", weight: itm.weight || ""
} }
})); }));
} }
// Create the actor in the current folder // Create the actor using v12 Documents API
try { try {
await Actor.create(actorData); const created = await Actor.createDocuments([actorData], {parent: game.actors});
ui.notifications.info(`${actorData.name} imported successfully!`); ui.notifications.info(`${actorData.name} imported successfully!`);
// Optionally, highlight/select the imported actor
if (created.length && app) {
app._lastSelected = [created[0].id];
app.render();
}
} catch (e) { } catch (e) {
console.error(e); console.error(e);
ui.notifications.error("Actor import failed."); ui.notifications.error("Actor import failed.");
} }
} }
async function importShadowdarklingsItem(data) { // Items (single or list)
async function importShadowdarklingsItem(data, app) {
// Accept single or array of items // Accept single or array of items
const items = Array.isArray(data) ? data : [data]; const items = Array.isArray(data) ? data : [data];
let created = [];
for (let itm of items) { for (let itm of items) {
if (!itm.name) continue; if (!itm.name) continue;
const itemData = { const itemData = {
name: itm.name, name: itm.name,
type: itm.type || "equipment", type: itm.type || "equipment", // Check your Shadowdark item types!
system: { system: {
description: { value: itm.description || "" }, description: { value: itm.description || "" },
// Add other fields as needed quantity: itm.quantity || 1
}, }
}; };
try { try {
await Item.create(itemData); const docs = await Item.createDocuments([itemData], {parent: game.items});
created.push(...docs);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
ui.notifications.warn(`Failed to import item: ${itm.name}`); ui.notifications.warn(`Failed to import item: ${itm.name}`);
} }
} }
ui.notifications.info("Items imported."); if (created.length > 0) ui.notifications.info("Items imported.");
} }