148 lines
4.5 KiB
JavaScript
148 lines
4.5 KiB
JavaScript
import { NPC_ROLLTABLE_DEFINITIONS } from './data/npcTables.js';
|
||
|
||
const MODULE_ID = 'mgt2-compendium-amiral-denisov';
|
||
const PACK_ID = `${MODULE_ID}.tables-pnj`;
|
||
const WORLD_FOLDER_NAME = 'MGT2 — Tables PNJ';
|
||
const ROLLTABLES_VERSION = 1;
|
||
|
||
function entryText(entry) {
|
||
if (typeof entry.d66 === 'number') return `[${entry.d66}] ${entry.text}`;
|
||
if (typeof entry.roll === 'number') return `[${entry.roll}] ${entry.text}`;
|
||
return entry.text;
|
||
}
|
||
|
||
function buildTableResults(entries) {
|
||
return entries.map((entry, index) => ({
|
||
type: CONST.TABLE_RESULT_TYPES.TEXT,
|
||
text: entryText(entry),
|
||
weight: 1,
|
||
range: [index + 1, index + 1],
|
||
drawn: false,
|
||
}));
|
||
}
|
||
|
||
function buildTableData(definition, extra = {}) {
|
||
return {
|
||
name: definition.name,
|
||
description: 'Table synchronisée automatiquement depuis les règles PNJ du module.',
|
||
formula: definition.formula,
|
||
replacement: true,
|
||
displayRoll: true,
|
||
results: buildTableResults(definition.entries),
|
||
flags: {
|
||
[MODULE_ID]: {
|
||
tableKey: definition.key,
|
||
syncVersion: ROLLTABLES_VERSION,
|
||
},
|
||
},
|
||
...extra,
|
||
};
|
||
}
|
||
|
||
function normalizeResults(results) {
|
||
return results.map((result) => ({
|
||
type: result.type,
|
||
text: result.name ?? result.description ?? result.text ?? '',
|
||
weight: result.weight,
|
||
range: Array.from(result.range),
|
||
}));
|
||
}
|
||
|
||
function needsSync(existing, wanted) {
|
||
if (!existing) return true;
|
||
if (existing.name !== wanted.name) return true;
|
||
if (existing.formula !== wanted.formula) return true;
|
||
if (existing.replacement !== wanted.replacement) return true;
|
||
const currentVersion = existing.getFlag(MODULE_ID, 'syncVersion');
|
||
if (currentVersion !== ROLLTABLES_VERSION) return true;
|
||
return JSON.stringify(normalizeResults(existing.results.contents)) !== JSON.stringify(normalizeResults(wanted.results));
|
||
}
|
||
|
||
function getExistingKey(document) {
|
||
return document.getFlag(MODULE_ID, 'tableKey') ?? document.name;
|
||
}
|
||
|
||
async function syncCompendiumPack() {
|
||
const pack = game.packs.get(PACK_ID);
|
||
if (!pack) throw new Error(`Pack introuvable : ${PACK_ID}`);
|
||
|
||
await pack.getIndex();
|
||
const existingDocs = await pack.getDocuments();
|
||
const existingByKey = new Map(existingDocs.map((doc) => [getExistingKey(doc), doc]));
|
||
const toCreate = [];
|
||
const toUpdate = [];
|
||
const wasLocked = pack.locked;
|
||
|
||
try {
|
||
if (wasLocked && typeof pack.configure === 'function') await pack.configure({ locked: false });
|
||
|
||
for (const definition of NPC_ROLLTABLE_DEFINITIONS) {
|
||
const wanted = buildTableData(definition);
|
||
const existing = existingByKey.get(definition.key);
|
||
if (!existing) {
|
||
toCreate.push(wanted);
|
||
continue;
|
||
}
|
||
if (needsSync(existing, wanted)) {
|
||
toUpdate.push({
|
||
_id: existing.id,
|
||
...wanted,
|
||
});
|
||
}
|
||
}
|
||
|
||
if (toCreate.length) await RollTable.createDocuments(toCreate, { pack: pack.collection });
|
||
if (toUpdate.length) await RollTable.updateDocuments(toUpdate, { pack: pack.collection });
|
||
} finally {
|
||
if (wasLocked && typeof pack.configure === 'function') await pack.configure({ locked: true });
|
||
}
|
||
}
|
||
|
||
async function getWorldFolder() {
|
||
let folder = game.folders.find((entry) => entry.type === 'RollTable' && entry.name === WORLD_FOLDER_NAME);
|
||
if (folder) return folder;
|
||
return Folder.create({
|
||
name: WORLD_FOLDER_NAME,
|
||
type: 'RollTable',
|
||
color: '#c9a227',
|
||
});
|
||
}
|
||
|
||
async function syncWorldFallback() {
|
||
const folder = await getWorldFolder();
|
||
const existingDocs = game.tables.filter((doc) => doc.folder?.id === folder.id);
|
||
const existingByKey = new Map(existingDocs.map((doc) => [getExistingKey(doc), doc]));
|
||
const toCreate = [];
|
||
const toUpdate = [];
|
||
|
||
for (const definition of NPC_ROLLTABLE_DEFINITIONS) {
|
||
const wanted = buildTableData(definition, { folder: folder.id });
|
||
const existing = existingByKey.get(definition.key);
|
||
if (!existing) {
|
||
toCreate.push(wanted);
|
||
continue;
|
||
}
|
||
if (needsSync(existing, wanted)) {
|
||
toUpdate.push({
|
||
_id: existing.id,
|
||
...wanted,
|
||
});
|
||
}
|
||
}
|
||
|
||
if (toCreate.length) await RollTable.createDocuments(toCreate);
|
||
if (toUpdate.length) await RollTable.updateDocuments(toUpdate);
|
||
}
|
||
|
||
export async function syncNpcRollTables() {
|
||
if (!game.user?.isGM) return;
|
||
|
||
try {
|
||
await syncCompendiumPack();
|
||
} catch (error) {
|
||
console.error(`${MODULE_ID} | Échec de synchronisation du pack RollTable`, error);
|
||
await syncWorldFallback();
|
||
ui.notifications.warn('Tables PNJ synchronisées dans le monde courant (fallback) — le pack de compendium n’a pas pu être mis à jour.');
|
||
}
|
||
}
|