407 lines
11 KiB
JavaScript
407 lines
11 KiB
JavaScript
import { inferWeaponSkillFromName } from './mgt2eSkills.js';
|
|
|
|
const MODULE_ID = 'mgt2-compendium-amiral-denisov';
|
|
const MIGRATION_SETTING = 'mgt2eMigrationVersion';
|
|
const MIGRATION_VERSION = 2;
|
|
|
|
const ITEM_PACKS = [
|
|
'armures',
|
|
'competences',
|
|
'maladie-poison-and-blessure',
|
|
'objet',
|
|
'equipement',
|
|
'ordinateur',
|
|
'contenant-sac-coffre',
|
|
'espece',
|
|
'armes',
|
|
'talents-psioniques',
|
|
'carrieres',
|
|
];
|
|
|
|
Hooks.once('init', () => {
|
|
game.settings.register(MODULE_ID, MIGRATION_SETTING, {
|
|
scope: 'world',
|
|
config: false,
|
|
type: Number,
|
|
default: 0,
|
|
});
|
|
});
|
|
|
|
Hooks.once('ready', async () => {
|
|
if (game.system?.id !== 'mgt2e' || !game.user?.isGM) return;
|
|
|
|
const currentVersion = game.settings.get(MODULE_ID, MIGRATION_SETTING) ?? 0;
|
|
if (currentVersion >= MIGRATION_VERSION) return;
|
|
|
|
ui.notifications.info('Migration mgt2e du module en cours...');
|
|
|
|
try {
|
|
for (const packName of ITEM_PACKS) {
|
|
await migrateItemPack(packName);
|
|
}
|
|
|
|
await game.settings.set(MODULE_ID, MIGRATION_SETTING, MIGRATION_VERSION);
|
|
ui.notifications.info('Migration mgt2e du module terminée.');
|
|
} catch (error) {
|
|
console.error(`${MODULE_ID} | Migration mgt2e impossible`, error);
|
|
ui.notifications.error(`Migration mgt2e inachevee : ${error.message}`);
|
|
}
|
|
});
|
|
|
|
async function migrateItemPack(packName) {
|
|
const collection = `${MODULE_ID}.${packName}`;
|
|
const pack = game.packs.get(collection);
|
|
if (!pack || pack.documentName !== 'Item') return;
|
|
|
|
const originalLocked = pack.locked;
|
|
if (originalLocked) {
|
|
await pack.configure({ locked: false });
|
|
}
|
|
|
|
try {
|
|
const documents = await pack.getDocuments();
|
|
const folderNames = buildFolderNameMap(pack);
|
|
const failures = [];
|
|
|
|
for (const document of documents) {
|
|
const folderId = document.folder?.id ?? document.folder ?? null;
|
|
const update = buildMigratedItemData(document, packName, folderNames.get(folderId));
|
|
if (!update) continue;
|
|
|
|
try {
|
|
await document.update(update, { diff: false, recursive: false });
|
|
} catch (error) {
|
|
failures.push(`${document.name}: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
if (failures.length) {
|
|
throw new Error(`${packName} (${failures.length} erreurs) — ${failures.slice(0, 5).join(' | ')}`);
|
|
}
|
|
} finally {
|
|
if (originalLocked) {
|
|
await pack.configure({ locked: true });
|
|
}
|
|
}
|
|
}
|
|
|
|
function buildFolderNameMap(pack) {
|
|
const folders = new Map();
|
|
for (const folder of pack.folders ?? []) {
|
|
folders.set(folder.id, folder.name ?? '');
|
|
}
|
|
return folders;
|
|
}
|
|
|
|
function buildMigratedItemData(document, packName, folderName = '') {
|
|
const legacyType = document.type;
|
|
const folderId = document.folder?.id ?? document.folder ?? null;
|
|
const common = {
|
|
name: document.name,
|
|
img: document.img,
|
|
flags: foundry.utils.mergeObject(document.flags ?? {}, {
|
|
[MODULE_ID]: {
|
|
legacyType,
|
|
migratedToMgt2e: MIGRATION_VERSION,
|
|
},
|
|
}),
|
|
folder: folderId,
|
|
sort: document.sort,
|
|
ownership: document.ownership,
|
|
};
|
|
|
|
switch (legacyType) {
|
|
case 'armor':
|
|
return {
|
|
...common,
|
|
type: 'armour',
|
|
system: buildArmourSystem(document),
|
|
};
|
|
case 'weapon':
|
|
return {
|
|
...common,
|
|
type: 'weapon',
|
|
system: buildWeaponSystem(document),
|
|
};
|
|
case 'equipment':
|
|
return {
|
|
...common,
|
|
type: isAugmentFolder(folderName) ? 'augment' : 'item',
|
|
system: isAugmentFolder(folderName) ? buildAugmentSystem(document) : buildSimpleItemSystem(document),
|
|
};
|
|
case 'computer':
|
|
return {
|
|
...common,
|
|
type: 'hardware',
|
|
system: buildHardwareSystem(document),
|
|
};
|
|
case 'item':
|
|
if (document.system?.subType === 'software') {
|
|
return {
|
|
...common,
|
|
type: 'software',
|
|
system: buildSoftwareSystem(document),
|
|
};
|
|
}
|
|
return {
|
|
...common,
|
|
type: 'item',
|
|
system: buildSimpleItemSystem(document),
|
|
};
|
|
case 'career':
|
|
return {
|
|
...common,
|
|
type: 'item',
|
|
system: buildReferenceItemSystem(document, 'Carriere', packName),
|
|
};
|
|
case 'talent':
|
|
return {
|
|
...common,
|
|
type: 'item',
|
|
system: buildReferenceItemSystem(document, document.system?.subType === 'psionic' ? 'Talent psionique' : 'Competence', packName),
|
|
};
|
|
case 'disease':
|
|
return {
|
|
...common,
|
|
type: 'item',
|
|
system: buildDiseaseItemSystem(document),
|
|
};
|
|
default:
|
|
return {
|
|
...common,
|
|
type: 'item',
|
|
system: buildReferenceItemSystem(document, legacyType, packName),
|
|
};
|
|
}
|
|
}
|
|
|
|
function buildWeaponSystem(document) {
|
|
const system = document.system ?? {};
|
|
const traits = normalizeTraits(system.traits);
|
|
const melee = Boolean(system.range?.isMelee);
|
|
|
|
return {
|
|
tl: normalizeTl(system.tl),
|
|
weight: toNumber(system.weight),
|
|
cost: toNumber(system.cost),
|
|
notes: traits.notes,
|
|
active: Boolean(system.equipped),
|
|
quantity: toNumber(system.quantity, 1),
|
|
status: system.trash ? 'broken' : null,
|
|
legality: 9,
|
|
weapon: {
|
|
scale: 'traveller',
|
|
range: melee ? 0 : toNumber(system.range?.value),
|
|
minRange: 0,
|
|
damage: normalizeDamage(system.damage),
|
|
magazine: toNumber(system.magazine),
|
|
ammo: toNumber(system.magazine),
|
|
magazineCost: toNumber(system.magazineCost),
|
|
characteristic: melee ? 'STR' : 'DEX',
|
|
skill: inferWeaponSkillFromName(document.name, melee),
|
|
parryBonus: 0,
|
|
damageBonus: melee ? 'STR' : '',
|
|
damageType: 'standard',
|
|
attackBonus: 0,
|
|
traits: traits.names,
|
|
},
|
|
description: formatDescription(system.description),
|
|
};
|
|
}
|
|
|
|
function buildArmourSystem(document) {
|
|
const system = document.system ?? {};
|
|
return {
|
|
tl: normalizeTl(system.tl),
|
|
weight: toNumber(system.weight),
|
|
cost: toNumber(system.cost),
|
|
notes: buildLines([
|
|
system.requireSkillLevel != null ? `Combi requise: ${system.requireSkillLevel}` : '',
|
|
]),
|
|
active: Boolean(system.equipped),
|
|
quantity: toNumber(system.quantity, 1),
|
|
status: system.trash ? 'broken' : null,
|
|
legality: 9,
|
|
armour: {
|
|
protection: toNumber(String(system.protection ?? '').replace(/[^\d-]/g, '')),
|
|
otherProtection: 0,
|
|
otherTypes: '',
|
|
rad: toNumber(system.radiations),
|
|
archaic: 0,
|
|
skill: toNumber(system.requireSkillLevel) > 0 ? 'vaccsuit' : '',
|
|
duration: 0,
|
|
slots: 0,
|
|
form: 'standard',
|
|
layered: 0,
|
|
ablat: 0,
|
|
powered: Boolean(system.powered) ? 1 : 0,
|
|
psi: 0,
|
|
worn: Boolean(system.equipped) ? 1 : 0,
|
|
},
|
|
description: formatDescription(system.description),
|
|
};
|
|
}
|
|
|
|
function buildSimpleItemSystem(document) {
|
|
const system = document.system ?? {};
|
|
return {
|
|
tl: normalizeTl(system.tl),
|
|
weight: toNumber(system.weight),
|
|
cost: toNumber(system.cost),
|
|
notes: '',
|
|
active: Boolean(system.equipped),
|
|
quantity: toNumber(system.quantity, 1),
|
|
status: system.trash ? 'broken' : null,
|
|
legality: 9,
|
|
description: formatDescription(system.description),
|
|
};
|
|
}
|
|
|
|
function buildAugmentSystem(document) {
|
|
const system = buildSimpleItemSystem(document);
|
|
return system;
|
|
}
|
|
|
|
function buildHardwareSystem(document) {
|
|
const system = document.system ?? {};
|
|
return {
|
|
tl: normalizeTl(system.tl),
|
|
weight: toNumber(system.weight),
|
|
cost: toNumber(system.cost),
|
|
notes: buildLines([
|
|
system.overload ? 'Surcharge: oui' : '',
|
|
system.processing != null ? `Traitement: ${system.processing}` : '',
|
|
system.processingUsed != null ? `Traitement utilise: ${system.processingUsed}` : '',
|
|
]),
|
|
active: false,
|
|
quantity: toNumber(system.quantity, 1),
|
|
status: system.trash ? 'broken' : null,
|
|
legality: 9,
|
|
hardware: {
|
|
system: 'computer',
|
|
tons: 0,
|
|
power: toNumber(system.processing),
|
|
rating: inferRating(document.name),
|
|
variables: {
|
|
max: 0,
|
|
tl: normalizeTl(system.tl),
|
|
cost: toNumber(system.cost),
|
|
},
|
|
},
|
|
description: formatDescription(system.description),
|
|
};
|
|
}
|
|
|
|
function buildSoftwareSystem(document) {
|
|
const system = document.system ?? {};
|
|
return {
|
|
quantity: toNumber(system.quantity, 1),
|
|
tl: normalizeTl(system.tl),
|
|
software: {
|
|
bandwidth: toNumber(system.software?.bandwidth),
|
|
},
|
|
cost: toNumber(system.cost),
|
|
description: formatDescription(system.description),
|
|
};
|
|
}
|
|
|
|
function buildReferenceItemSystem(document, category, packName) {
|
|
const system = document.system ?? {};
|
|
const isSkillReference = packName === 'competences';
|
|
return {
|
|
tl: normalizeTl(system.tl),
|
|
weight: toNumber(system.weight),
|
|
cost: toNumber(system.cost),
|
|
notes: buildLines([
|
|
isSkillReference ? 'Référence documentaire uniquement — les compétences de jeu utilisent désormais actor.system.skills et la localisation MGT2.Skills.*.' : '',
|
|
category ? `Categorie legacy: ${category}` : '',
|
|
system.subType ? `Sous-type legacy: ${system.subType}` : '',
|
|
system.level != null ? `Niveau legacy: ${system.level}` : '',
|
|
system.assignment ? `Affectations: ${system.assignment}` : '',
|
|
]),
|
|
active: false,
|
|
quantity: toNumber(system.quantity, 1),
|
|
status: null,
|
|
legality: 9,
|
|
description: isSkillReference
|
|
? formatDescription([system.description, '<p><strong>Note mgt2e :</strong> cette entrée sert de référence documentaire. Les compétences jouables sont natives à la fiche de personnage `mgt2e`.</p>'].filter(Boolean).join('\n\n'))
|
|
: formatDescription(system.description),
|
|
};
|
|
}
|
|
|
|
function buildDiseaseItemSystem(document) {
|
|
const system = document.system ?? {};
|
|
return {
|
|
tl: '0',
|
|
weight: 0,
|
|
cost: 0,
|
|
notes: buildLines([
|
|
system.subType ? `Sous-type: ${system.subType}` : '',
|
|
system.difficulty ? `Difficulte: ${system.difficulty}` : '',
|
|
system.damage ? `Degats: ${system.damage}` : '',
|
|
system.interval ? `Intervalle: ${system.interval}` : '',
|
|
]),
|
|
active: false,
|
|
quantity: 1,
|
|
status: null,
|
|
legality: 9,
|
|
description: formatDescription(system.description),
|
|
};
|
|
}
|
|
|
|
function isAugmentFolder(folderName) {
|
|
return /augmentation/i.test(folderName ?? '');
|
|
}
|
|
|
|
function normalizeTl(value) {
|
|
const match = String(value ?? '').match(/(\d+)/);
|
|
return match ? match[1] : '0';
|
|
}
|
|
|
|
function normalizeDamage(value) {
|
|
return String(value ?? '1D6').replace(/d/g, 'D');
|
|
}
|
|
|
|
function toNumber(value, fallback = 0) {
|
|
const parsed = Number.parseInt(String(value ?? ''), 10);
|
|
return Number.isFinite(parsed) ? parsed : fallback;
|
|
}
|
|
|
|
function inferRating(name) {
|
|
const match = String(name ?? '').match(/\/(\d+)/);
|
|
return match ? Number.parseInt(match[1], 10) : 0;
|
|
}
|
|
|
|
function normalizeTraits(traits) {
|
|
if (!Array.isArray(traits) || !traits.length) return { names: '', notes: '' };
|
|
|
|
const names = traits
|
|
.map((trait) => trait?.name ?? trait)
|
|
.filter(Boolean)
|
|
.join(', ');
|
|
const notes = traits
|
|
.filter((trait) => trait?.description)
|
|
.map((trait) => `${trait.name}: ${trait.description}`)
|
|
.join('\n');
|
|
|
|
return { names, notes };
|
|
}
|
|
|
|
function formatDescription(value) {
|
|
const text = String(value ?? '').trim();
|
|
if (!text) return '';
|
|
if (text.includes('<')) return text;
|
|
|
|
const paragraphs = text
|
|
.split(/\n{2,}/)
|
|
.map((part) => part.trim())
|
|
.filter(Boolean)
|
|
.map((part) => `<p>${part.replace(/\n/g, '<br>')}</p>`);
|
|
|
|
return paragraphs.join('');
|
|
}
|
|
|
|
function buildLines(lines) {
|
|
return lines.filter(Boolean).join('\n');
|
|
}
|