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, '

Note mgt2e : cette entrée sert de référence documentaire. Les compétences jouables sont natives à la fiche de personnage `mgt2e`.

'].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) => `

${part.replace(/\n/g, '
')}

`); return paragraphs.join(''); } function buildLines(lines) { return lines.filter(Boolean).join('\n'); }