Files
shadowdark-importer/shadowdarklings-importer.js

167 lines
5.0 KiB
JavaScript
Raw Normal View History

2025-05-16 05:56:22 -05:00
// shadowdarklings-importer.js
2025-05-16 06:21:19 -05:00
Hooks.once("ready", function () {
2025-05-16 05:56:22 -05:00
// 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 = $(
`<button class="shadowdarklings-importer"><i class="fas fa-file-import"></i> Shadowdarklings Import</button>`
);
importBtn.on("click", async () => {
2025-05-16 06:21:19 -05:00
const dialog = new ShadowdarklingsImportDialog(type, app);
2025-05-16 05:56:22 -05:00
dialog.render(true);
});
html.find(".directory-footer").append(importBtn);
}
2025-05-16 06:21:19 -05:00
// Upload dialog
2025-05-16 05:56:22 -05:00
class ShadowdarklingsImportDialog extends Dialog {
2025-05-16 06:21:19 -05:00
constructor(type, app) {
2025-05-16 05:56:22 -05:00
super({
title: `Import Shadowdarklings ${type === "actor" ? "Character/Monster" : "Item"}`,
content: `
<form>
<div class="form-group">
<label>Upload Shadowdarklings JSON:</label>
<input type="file" name="json" accept="application/json"/>
</div>
</form>
`,
buttons: {
import: {
icon: '<i class="fas fa-file-import"></i>',
label: "Import",
callback: (html) => this.importJSON(html)
},
cancel: {
icon: '<i class="fas fa-times"></i>',
label: "Cancel"
}
},
default: "import"
});
this.type = type;
2025-05-16 06:21:19 -05:00
this.app = app;
2025-05-16 05:56:22 -05:00
}
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;
}
if (this.type === "actor") {
2025-05-16 06:21:19 -05:00
return importShadowdarklingsActor(json, this.app);
2025-05-16 05:56:22 -05:00
} else {
2025-05-16 06:21:19 -05:00
return importShadowdarklingsItem(json, this.app);
2025-05-16 05:56:22 -05:00
}
}
}
2025-05-16 06:21:19 -05:00
// Main actor importer
async function importShadowdarklingsActor(data, app) {
// Use system-defined actor types: 'pc' (player), 'npc' (monsters/opponents)
// Try to discern if this is a PC or NPC/Monster.
let actorType = "npc";
// If class or level is present, it's likely a PC
if (data.class || data.level || data.background || data.player) actorType = "pc";
// 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
2025-05-16 05:56:22 -05:00
2025-05-16 06:21:19 -05:00
// Setup the document data
2025-05-16 05:56:22 -05:00
const actorData = {
name: data.name || "Imported Character",
2025-05-16 06:21:19 -05:00
type: actorType,
2025-05-16 05:56:22 -05:00
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 },
2025-05-16 06:21:19 -05:00
hp: { value: data.hp, max: data.hp || data.maxHp, min: 0 },
2025-05-16 05:56:22 -05:00
level: { value: data.level },
class: { value: data.class },
2025-05-16 06:21:19 -05:00
ancestry: { value: data.race },
alignment: { value: data.alignment }
// Extend as needed for your Shadowdark system version
2025-05-16 05:56:22 -05:00
},
2025-05-16 06:21:19 -05:00
// Optionally add image
// img: data.img || "icons/svg/mystery-man.svg"
2025-05-16 05:56:22 -05:00
};
2025-05-16 06:21:19 -05:00
// Try inventory import (if system supports embedded items in actors)
2025-05-16 05:56:22 -05:00
if (Array.isArray(data.inventory)) {
2025-05-16 06:21:19 -05:00
actorData.items = data.inventory.filter(i=>i.name).map(itm => ({
2025-05-16 05:56:22 -05:00
name: itm.name,
2025-05-16 06:21:19 -05:00
type: "equipment", // Or "gear" or your actual item type in Shadowdark
2025-05-16 05:56:22 -05:00
system: {
description: { value: itm.description || "" },
quantity: itm.quantity || 1,
value: itm.value || "",
2025-05-16 06:21:19 -05:00
weight: itm.weight || ""
2025-05-16 05:56:22 -05:00
}
}));
}
2025-05-16 06:21:19 -05:00
// Create the actor using v12 Documents API
2025-05-16 05:56:22 -05:00
try {
2025-05-16 06:21:19 -05:00
const created = await Actor.createDocuments([actorData], {parent: game.actors});
2025-05-16 05:56:22 -05:00
ui.notifications.info(`${actorData.name} imported successfully!`);
2025-05-16 06:21:19 -05:00
// Optionally, highlight/select the imported actor
if (created.length && app) {
app._lastSelected = [created[0].id];
app.render();
}
2025-05-16 05:56:22 -05:00
} catch (e) {
console.error(e);
ui.notifications.error("Actor import failed.");
}
}
2025-05-16 06:21:19 -05:00
// Items (single or list)
async function importShadowdarklingsItem(data, app) {
2025-05-16 05:56:22 -05:00
// Accept single or array of items
const items = Array.isArray(data) ? data : [data];
2025-05-16 06:21:19 -05:00
let created = [];
2025-05-16 05:56:22 -05:00
for (let itm of items) {
if (!itm.name) continue;
const itemData = {
name: itm.name,
2025-05-16 06:21:19 -05:00
type: itm.type || "equipment", // Check your Shadowdark item types!
2025-05-16 05:56:22 -05:00
system: {
description: { value: itm.description || "" },
2025-05-16 06:21:19 -05:00
quantity: itm.quantity || 1
}
2025-05-16 05:56:22 -05:00
};
try {
2025-05-16 06:21:19 -05:00
const docs = await Item.createDocuments([itemData], {parent: game.items});
created.push(...docs);
2025-05-16 05:56:22 -05:00
} catch (e) {
console.error(e);
ui.notifications.warn(`Failed to import item: ${itm.name}`);
}
}
2025-05-16 06:21:19 -05:00
if (created.length > 0) ui.notifications.info("Items imported.");
2025-05-16 05:56:22 -05:00
}