Files
sla_industries_MS/module/windows/sla-world-tools.js
T
tevans 2758be6096 Add NPC Desk: generator window, creature dossier, world tools integration
New files:
- module/windows/sla-npc-generator.js — NPC generator window; archetype, species, quality, affiliation, dept/cell, role, sector, disposition, threat, loadout preset; auto-creates creature actors into SLA Mothership/NPCs folder; auto-assigns portrait if portrait pack active; seeds basic skills, weapons, armor and gear
- templates/dialogs/sla-npc-generator.html — form template for the NPC Desk window

Updated files:
- template.json — extended creature actor data with NPC dossier fields (role, affiliation, department, sector, threat, summary)
- templates/actor/creature-sheet.html — editable NPC dossier block on creature sheet
- module/actor/creature-sheet.js — wired dossier fields into sheet data
- module/mosh.js — exposed generator via system API
- module/windows/sla-world-tools.js — sidebar button to open NPC Desk
- templates/dialogs/sla-world-tools.html — Open NPC Desk entry in world tools dialog
- css/mosh.css — styling for NPC Desk and dossier block
2026-04-15 17:12:46 +01:00

162 lines
5.3 KiB
JavaScript

import { SLAMothershipGenerator } from "./sla-character-generator.js";
import { SLANpcGeneratorApp } from "./sla-npc-generator.js";
const SYSTEM_ID = "sla-mothership";
export class SLAWorldToolsApp extends FormApplication {
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
id: "sla-mothership-world-tools",
classes: ["mosh", "sheet", "sla-world-tools"],
template: "systems/sla-mothership/templates/dialogs/sla-world-tools.html",
width: 560,
height: "auto",
submitOnChange: false,
closeOnSubmit: false
});
}
get title() {
return "SLA Mothership Tools";
}
async getData() {
const seededItems = game.items.filter((item) => item.flags?.[SYSTEM_ID]?.slaSeed?.key);
const actors = game.actors.filter((actor) => actor.type === "character");
const creatures = game.actors.filter((actor) => actor.type === "creature");
const ebbActors = actors.filter((actor) => ["Ebon", "Brain Waster"].includes(String(actor.system?.sla?.species?.value ?? "").trim()));
return {
seededItems: seededItems.length,
skillCount: seededItems.filter((item) => item.type === "skill").length,
weaponCount: seededItems.filter((item) => item.type === "weapon").length,
gearCount: seededItems.filter((item) => item.flags?.[SYSTEM_ID]?.slaSeed?.kind === "gear").length,
actorCount: actors.length,
creatureCount: creatures.length,
starterCount: actors.filter((actor) => actor.flags?.[SYSTEM_ID]?.slaStarter).length,
ebbActorCount: ebbActors.length,
quickBattleMapBuilderReady: Boolean(game.modules?.get("quick-battlemap-builder")?.active && game.quickBattleMapBuilder?.open)
};
}
activateListeners(html) {
super.activateListeners(html);
html.find(".seed-world").on("click", async () => {
await game.slaMothership.seedWorldContent({ overwrite: false, notify: true });
this.render(false);
});
html.find(".refresh-world").on("click", async () => {
await game.slaMothership.seedWorldContent({ overwrite: true, notify: true });
this.render(false);
});
html.find(".open-generator").on("click", () => {
game.slaMothership.openGenerator();
});
html.find(".open-npc-desk").on("click", () => {
game.slaMothership.openNpcDesk();
});
html.find(".generate-random").on("click", async () => {
await game.slaMothership.createRandomOperative({ openSheet: true });
this.render(false);
});
html.find(".generate-random-ebb").on("click", async () => {
await SLAMothershipGenerator.createRandomEbbOperative({ openSheet: true });
this.render(false);
});
html.find(".generate-random-standard").on("click", async () => {
await SLAMothershipGenerator.createRandomNonEbbOperative({ openSheet: true });
this.render(false);
});
html.find(".starter-squad").on("click", async () => {
await game.slaMothership.createStarterSquad({ overwrite: false, notify: true });
this.render(false);
});
html.find(".open-battlemap-builder").on("click", () => {
game.quickBattleMapBuilder?.open?.();
});
}
async _updateObject() {}
static open() {
return new SLAWorldToolsApp().render({ force: true });
}
}
export function installSlaSidebarButtons(app, html) {
const canManage = Boolean(game.user?.isGM);
if (!canManage) return;
const root = $(html);
if (root.find(".sla-create-random-operative").length) return;
const toolContainer = findToolContainer(root);
if (!toolContainer?.length) return;
const toolsButton = $(`
<button type="button" class="sla-world-tools-launch">
<i class="fas fa-toolbox"></i> SLA Tools
</button>
`);
toolsButton.on("click", () => SLAWorldToolsApp.open());
const createButton = $(`
<button type="button" class="sla-create-operative">
<i class="fas fa-user-secret"></i> Create SLA Operative
</button>
`);
createButton.on("click", () => SLAMothershipGenerator.open());
const randomButton = $(`
<button type="button" class="sla-create-random-operative">
<i class="fas fa-dice"></i> Random SLA Operative
</button>
`);
randomButton.on("click", async () => {
await SLAMothershipGenerator.createRandomOperative({ openSheet: true });
});
const refreshButton = $(`
<button type="button" class="sla-refresh-content">
<i class="fas fa-rotate"></i> Refresh Content
</button>
`);
refreshButton.on("click", async () => {
await game.slaMothership.seedWorldContent({ overwrite: true, notify: true });
});
const npcButton = $(`
<button type="button" class="sla-open-npc-desk">
<i class="fas fa-id-badge"></i> NPC Desk
</button>
`);
npcButton.on("click", () => SLANpcGeneratorApp.open());
toolContainer.append(randomButton, createButton, npcButton, toolsButton, refreshButton);
}
function findToolContainer(root) {
const header = root.find(".directory-header, header").first();
if (!header.length) return null;
const existing = header.find(".sla-sidebar-actions");
if (existing.length) return existing.first();
const actionRow = header.find(".header-actions, .action-buttons").first();
const toolRow = $('<div class="sla-sidebar-actions"></div>');
if (actionRow.length) {
actionRow.after(toolRow);
} else {
header.prepend(toolRow);
}
return toolRow;
}