@alpaca-software/40kdc-data 0.3.2 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/README.md +12 -6
  2. package/dist/bundle-schemas.d.ts.map +1 -1
  3. package/dist/bundle-schemas.js +17 -0
  4. package/dist/bundle-schemas.js.map +1 -1
  5. package/dist/cli.js +5 -0
  6. package/dist/cli.js.map +1 -1
  7. package/dist/codegen-data.js +1 -0
  8. package/dist/codegen-data.js.map +1 -1
  9. package/dist/commands/populate-base-sizes.d.ts +2 -0
  10. package/dist/commands/populate-base-sizes.d.ts.map +1 -0
  11. package/dist/commands/populate-base-sizes.js +158 -0
  12. package/dist/commands/populate-base-sizes.js.map +1 -0
  13. package/dist/convert-faction.d.ts +3 -1
  14. package/dist/convert-faction.d.ts.map +1 -1
  15. package/dist/convert-faction.js +49 -16
  16. package/dist/convert-faction.js.map +1 -1
  17. package/dist/converters/base-size-bridge.d.ts +122 -0
  18. package/dist/converters/base-size-bridge.d.ts.map +1 -0
  19. package/dist/converters/base-size-bridge.js +198 -0
  20. package/dist/converters/base-size-bridge.js.map +1 -0
  21. package/dist/converters/base-size-guide-extract.d.ts +11 -0
  22. package/dist/converters/base-size-guide-extract.d.ts.map +1 -0
  23. package/dist/converters/base-size-guide-extract.js +59 -0
  24. package/dist/converters/base-size-guide-extract.js.map +1 -0
  25. package/dist/converters/option-bridge.d.ts +36 -0
  26. package/dist/converters/option-bridge.d.ts.map +1 -0
  27. package/dist/converters/option-bridge.js +72 -0
  28. package/dist/converters/option-bridge.js.map +1 -0
  29. package/dist/converters/option-parser.d.ts +56 -0
  30. package/dist/converters/option-parser.d.ts.map +1 -0
  31. package/dist/converters/option-parser.js +209 -0
  32. package/dist/converters/option-parser.js.map +1 -0
  33. package/dist/converters/wargear-options.d.ts +55 -0
  34. package/dist/converters/wargear-options.d.ts.map +1 -0
  35. package/dist/converters/wargear-options.js +187 -0
  36. package/dist/converters/wargear-options.js.map +1 -0
  37. package/dist/data/bundle.generated.js +1 -1
  38. package/dist/data/bundle.generated.js.map +1 -1
  39. package/dist/data/dataset.d.ts +9 -1
  40. package/dist/data/dataset.d.ts.map +1 -1
  41. package/dist/data/dataset.js +14 -0
  42. package/dist/data/dataset.js.map +1 -1
  43. package/dist/data/entities.d.ts +3 -1
  44. package/dist/data/entities.d.ts.map +1 -1
  45. package/dist/data/entities.js +4 -0
  46. package/dist/data/entities.js.map +1 -1
  47. package/dist/data/index.d.ts +4 -0
  48. package/dist/data/index.d.ts.map +1 -1
  49. package/dist/data/index.js +4 -0
  50. package/dist/data/index.js.map +1 -1
  51. package/dist/data/loadout.d.ts +60 -0
  52. package/dist/data/loadout.d.ts.map +1 -0
  53. package/dist/data/loadout.js +135 -0
  54. package/dist/data/loadout.js.map +1 -0
  55. package/dist/data/types.d.ts +3 -1
  56. package/dist/data/types.d.ts.map +1 -1
  57. package/dist/data/types.js +1 -0
  58. package/dist/data/types.js.map +1 -1
  59. package/dist/export/rosterizer.js +1 -1
  60. package/dist/export/rosterizer.js.map +1 -1
  61. package/dist/gen-conformance.js +171 -0
  62. package/dist/gen-conformance.js.map +1 -1
  63. package/dist/generated.d.ts +135 -55
  64. package/dist/generated.d.ts.map +1 -1
  65. package/dist/generated.js.map +1 -1
  66. package/dist/import/rosterizer.d.ts +1 -1
  67. package/dist/import/rosterizer.js.map +1 -1
  68. package/dist/index.d.ts +3 -2
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +3 -3
  71. package/dist/index.js.map +1 -1
  72. package/dist/runner.d.ts +16 -0
  73. package/dist/runner.d.ts.map +1 -1
  74. package/dist/runner.js +216 -0
  75. package/dist/runner.js.map +1 -1
  76. package/dist/scoring/index.d.ts +28 -6
  77. package/dist/scoring/index.d.ts.map +1 -1
  78. package/dist/scoring/index.js +31 -7
  79. package/dist/scoring/index.js.map +1 -1
  80. package/dist/terrain/index.d.ts +2 -2
  81. package/dist/terrain/index.d.ts.map +1 -1
  82. package/dist/terrain/index.js +1 -1
  83. package/dist/terrain/index.js.map +1 -1
  84. package/dist/terrain/solve.d.ts +41 -0
  85. package/dist/terrain/solve.d.ts.map +1 -1
  86. package/dist/terrain/solve.js +100 -0
  87. package/dist/terrain/solve.js.map +1 -1
  88. package/dist/translate/condition.d.ts.map +1 -1
  89. package/dist/translate/condition.js +4 -0
  90. package/dist/translate/condition.js.map +1 -1
  91. package/dist/validate.d.ts.map +1 -1
  92. package/dist/validate.js +13 -5
  93. package/dist/validate.js.map +1 -1
  94. package/package.json +5 -5
  95. package/schemas/$defs/common.schema.json +14 -0
  96. package/schemas/core/secondary-card.schema.json +10 -0
  97. package/schemas/core/terrain-layout.schema.json +18 -0
  98. package/schemas/core/unit-composition.schema.json +5 -1
  99. package/schemas/core/unit.schema.json +2 -10
  100. package/schemas/core/wargear-option.schema.json +32 -6
  101. package/schemas/core/wargear.schema.json +24 -0
  102. package/schemas/enrichment/ability-dsl/condition.schema.json +3 -2
@@ -1 +1 @@
1
- {"version":3,"file":"convert-faction.js","sourceRoot":"","sources":["../src/convert-faction.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Generic faction converter: army-assist → 40kdc-data format.\n *\n * Usage: npx tsx tools/src/convert-faction.ts <faction-id>\n * Example: npx tsx tools/src/convert-faction.ts emperors-children\n *\n * Faction configs are registered via side-effect imports from ./converters/configs/.\n */\n\nimport { readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { resolve, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { nameToId, parseStratagemType, parsePlayerTurn, mapPhases } from \"./converters/id-generator.js\";\nimport { parseMove, parseTargetNumber, parseIntStat, parseInvuln } from \"./converters/stat-parser.js\";\nimport { findFactionViewIndex, getViewEntries, getPointsForView, splitIntoViews, type SourceAbility } from \"./converters/view-selector.js\";\nimport { buildWeaponRegistry, type SourceWargear } from \"./converters/weapon-dedup.js\";\nimport { getKeywordsForFaction, type SourceKeyword } from \"./converters/keyword-filter.js\";\nimport { type FactionConfig, getFactionConfig, listFactions } from \"./converters/faction-config.js\";\n\n// Register all faction configs\nimport \"./converters/configs/world-eaters.js\";\nimport \"./converters/configs/emperors-children.js\";\nimport \"./converters/configs/chaos-knights.js\";\nimport \"./converters/configs/imperial-knights.js\";\nimport \"./converters/configs/leagues-of-votann.js\";\nimport \"./converters/configs/drukhari.js\";\nimport \"./converters/configs/genestealer-cults.js\";\nimport \"./converters/configs/grey-knights.js\";\nimport \"./converters/configs/thousand-sons.js\";\nimport \"./converters/configs/death-guard.js\";\nimport \"./converters/configs/adeptus-custodes.js\";\nimport \"./converters/configs/adepta-sororitas.js\";\nimport \"./converters/configs/agents-of-the-imperium.js\";\nimport \"./converters/configs/adeptus-mechanicus.js\";\nimport \"./converters/configs/tau-empire.js\";\nimport \"./converters/configs/tyranids.js\";\nimport \"./converters/configs/necrons.js\";\nimport \"./converters/configs/chaos-daemons.js\";\nimport \"./converters/configs/orks.js\";\nimport \"./converters/configs/aeldari.js\";\nimport \"./converters/configs/chaos-space-marines.js\";\nimport \"./converters/configs/astra-militarum.js\";\nimport \"./converters/configs/adeptus-astartes.js\";\nimport \"./converters/configs/blood-angels.js\";\nimport \"./converters/configs/dark-angels.js\";\nimport \"./converters/configs/space-wolves.js\";\nimport \"./converters/configs/black-templars.js\";\nimport \"./converters/configs/deathwatch.js\";\nimport \"./converters/configs/ultramarines.js\";\nimport \"./converters/configs/imperial-fists.js\";\nimport \"./converters/configs/crimson-fists.js\";\nimport \"./converters/configs/iron-hands.js\";\nimport \"./converters/configs/raven-guard.js\";\nimport \"./converters/configs/salamanders.js\";\nimport \"./converters/configs/white-scars.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst ROOT = resolve(__dirname, \"../..\");\nconst SOURCE = resolve(process.env.HOME!, \"army-assist/src/assets/json\");\nconst GAME_VERSION = { edition: \"10th\", dataslate: \"2025-q3\" };\n\n// ─── Source data types ───────────────────────────────────────────────\n\ninterface SourceDatasheet {\n id: string;\n name: string;\n faction_id: string;\n loadout: string;\n transport: string;\n role: string;\n damaged_w: string;\n damaged_description: string;\n}\n\ninterface SourceModel {\n datasheet_id: string;\n line: string;\n name: string;\n M: string;\n T: string;\n Sv: string;\n inv_sv: string;\n inv_sv_descr: string;\n W: string;\n Ld: string;\n OC: string;\n base_size: string;\n base_size_descr: string;\n}\n\ninterface SourcePoints {\n datasheet_id: string;\n models: string;\n cost: string;\n}\n\ninterface SourceLeader {\n leader_id: string;\n attached_id: string;\n}\n\ninterface SourceEnhancement {\n id: string;\n name: string;\n faction_id: string;\n cost: string;\n detachment: string;\n phases: string[];\n}\n\ninterface SourceStratagem {\n id: string;\n name: string;\n faction_id: string;\n type: string;\n cp_cost: string;\n turn: string;\n phase: string;\n detachment: string;\n phases: string[];\n}\n\ninterface SourceDetachmentAbility {\n name: string;\n faction_id: string;\n detachment: string;\n phases: string[];\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────\n\nfunction readJSON<T>(filename: string): T {\n return JSON.parse(readFileSync(resolve(SOURCE, filename), \"utf-8\"));\n}\n\nfunction writeOutput(relPath: string, data: unknown): void {\n const outPath = resolve(ROOT, relPath);\n writeFileSync(outPath, JSON.stringify(data, null, 2) + \"\\n\");\n console.log(` ✓ ${relPath} (${Array.isArray(data) ? data.length : 1} entries)`);\n}\n\n/** Determine unit role from keywords and abilities. */\nfunction deriveRole(\n keywords: string[],\n abilities: SourceAbility[],\n name: string\n): string | undefined {\n const kw = new Set(keywords.map((k) => k.toLowerCase()));\n if (kw.has(\"epic hero\")) return \"epic-hero\";\n\n if (kw.has(\"character\")) return \"character\";\n const hasLeader = abilities.some(\n (a) => a.type === \"Core\" && a.name === \"Leader\"\n );\n if (hasLeader) return \"character\";\n\n if (kw.has(\"battleline\")) return \"battleline\";\n\n if (\n name.toLowerCase().includes(\"rhino\") ||\n kw.has(\"dedicated transport\")\n )\n return \"dedicated-transport\";\n\n return undefined;\n}\n\n/** Parse base size string. \"32mm\" → { shape: \"round\", diameter: 32 } */\nfunction parseBaseSize(\n s: string\n): { shape: string; diameter?: number; width?: number; length?: number } | undefined {\n if (!s || s.trim() === \"\") return undefined;\n const round = s.match(/(\\d+)\\s*mm/i);\n if (round) return { shape: \"round\", diameter: parseInt(round[1], 10) };\n const oval = s.match(/(\\d+)\\s*x\\s*(\\d+)/i);\n if (oval)\n return {\n shape: \"oval\",\n width: parseInt(oval[1], 10),\n length: parseInt(oval[2], 10),\n };\n return undefined;\n}\n\n/** Parse transport capacity from transport text. */\nfunction parseTransport(\n s: string\n): { capacity: number; keyword_restrictions?: string[]; exclusion_keywords?: string[] } | undefined {\n if (!s || s.trim() === \"\") return undefined;\n // Match \"capacity of N\" or \"N <FACTION> INFANTRY\"\n const capMatch = s.match(/capacity\\s*(?:of\\s*)?(\\d+)/i) || s.match(/(\\d+)\\s+\\S[\\S\\s]*?\\s+(?:infantry|model)/i);\n if (!capMatch) return undefined;\n const capacity = parseInt(capMatch[1], 10);\n const result: { capacity: number; keyword_restrictions?: string[]; exclusion_keywords?: string[] } = { capacity };\n\n if (/jump pack/i.test(s) && /cannot/i.test(s)) {\n result.exclusion_keywords = [\"Jump Pack\"];\n }\n return result;\n}\n\n// ─── Main conversion ─────────────────────────────────────────────────\n\nexport function convertFaction(config: FactionConfig): void {\n const { sourceFactionId, factionId, factionName, factionAbilityName } = config;\n\n console.log(`Converting ${factionName} (${sourceFactionId} → ${factionId})...`);\n console.log(\"Loading source data from army-assist...\");\n\n const datasheets = readJSON<SourceDatasheet[]>(\"Datasheets.json\");\n const allModels = readJSON<SourceModel[]>(\"Datasheets_models.json\");\n const allWargear = readJSON<SourceWargear[]>(\"Datasheets_wargear.json\");\n const allAbilities = readJSON<SourceAbility[]>(\"Datasheets_abilities.json\");\n const allKeywords = readJSON<SourceKeyword[]>(\"Datasheets_keywords.json\");\n const allPoints = readJSON<SourcePoints[]>(\"Datasheets_points.json\");\n const allLeaders = readJSON<SourceLeader[]>(\"Datasheets_leader.json\");\n const enhancements = readJSON<SourceEnhancement[]>(\"Enhancements.json\");\n const stratagems = readJSON<SourceStratagem[]>(\"Stratagems.json\");\n const detachmentAbilities = readJSON<SourceDetachmentAbility[]>(\n \"Detachment_abilities.json\"\n );\n\n // Filter to target faction, skip datasheets with no model data (metadata entries)\n const modelDatasheetIds = new Set(allModels.map((m) => m.datasheet_id));\n const factionDatasheets = datasheets.filter(\n (d) => d.faction_id === sourceFactionId && modelDatasheetIds.has(d.id)\n );\n const factionIds = new Set(factionDatasheets.map((d) => d.id));\n const idToName = new Map(factionDatasheets.map((d) => [d.id, d.name]));\n\n console.log(`Found ${factionDatasheets.length} ${factionName} datasheets\\n`);\n\n // ─── Determine view indices for shared units ───\n const viewIndices = new Map<string, number>();\n for (const ds of factionDatasheets) {\n const dsAbilities = allAbilities.filter(\n (a) => a.datasheet_id === ds.id\n );\n viewIndices.set(ds.id, findFactionViewIndex(dsAbilities, factionAbilityName));\n }\n\n // ─── Build units ───\n console.log(\"Converting units...\");\n const units: Record<string, unknown>[] = [];\n\n const unitWargearMap = new Map<string, SourceWargear[]>();\n const unitAbilityNames = new Map<string, string[]>();\n\n for (const ds of factionDatasheets) {\n const viewIdx = viewIndices.get(ds.id)!;\n\n // Models\n const dsModels = allModels.filter((m) => m.datasheet_id === ds.id);\n const viewModels = getViewEntries(dsModels, viewIdx);\n\n // Abilities (for role derivation and ability name collection)\n const dsAbilities = allAbilities.filter(\n (a) => a.datasheet_id === ds.id\n );\n const viewAbilities = getViewEntries(dsAbilities, viewIdx);\n\n const abilityNames = viewAbilities\n .filter((a) => a.type !== \"Faction\")\n .map((a) => a.name);\n unitAbilityNames.set(ds.id, [...new Set(abilityNames)]);\n\n // Keywords — use faction-aware filtering for shared units\n const dsKeywords = allKeywords.filter(\n (k) => k.datasheet_id === ds.id\n );\n const { factionKeywords, regularKeywords } = getKeywordsForFaction(\n dsKeywords,\n factionName\n );\n\n // Points — select the correct view for shared units\n const allDsPoints = allPoints.filter((p) => p.datasheet_id === ds.id);\n const dsAbilityViews = splitIntoViews(\n allAbilities.filter((a) => a.datasheet_id === ds.id)\n );\n const numViews = dsAbilityViews.length;\n const viewPoints = getPointsForView(allDsPoints, viewIdx, numViews);\n const dsPoints = viewPoints\n .map((p) => ({\n models: parseInt(p.models, 10),\n cost: parseInt(p.cost, 10),\n }))\n .sort((a, b) => a.models - b.models);\n\n // Wargear for this unit's view\n const dsWargear = allWargear.filter(\n (w) => w.datasheet_id === ds.id\n );\n const viewWargear = getViewEntries(dsWargear, viewIdx);\n unitWargearMap.set(ds.id, viewWargear);\n\n // Build stat profiles\n const profiles = viewModels.map((m) => {\n const profile: Record<string, unknown> = {\n name: m.name,\n M: parseMove(m.M),\n T: parseIntStat(m.T),\n W: parseIntStat(m.W),\n Sv: parseTargetNumber(m.Sv)!,\n invuln_sv: parseInvuln(m.inv_sv),\n Ld: parseTargetNumber(m.Ld)!,\n OC: parseIntStat(m.OC),\n };\n return profile;\n });\n\n const role = deriveRole(regularKeywords, viewAbilities, ds.name);\n const baseSize = parseBaseSize(\n viewModels[0]?.base_size ?? \"\"\n );\n const transport = parseTransport(ds.transport);\n\n const modelMin = dsPoints.length > 0 ? dsPoints[0].models : 1;\n const modelMax =\n dsPoints.length > 0 ? dsPoints[dsPoints.length - 1].models : 1;\n\n const unitId = nameToId(ds.name);\n const unit: Record<string, unknown> = {\n id: unitId,\n name: ds.name,\n faction_id: factionId,\n ...(role ? { role } : {}),\n profiles,\n points: dsPoints,\n keywords: regularKeywords,\n faction_keywords: factionKeywords,\n ...(baseSize ? { base_size_mm: baseSize } : {}),\n model_count: { min: modelMin, max: modelMax },\n weapon_ids: [],\n ability_ids: [],\n ...(transport ? { transport_capacity: transport } : {}),\n game_version: GAME_VERSION,\n is_legend: false,\n };\n\n units.push(unit);\n }\n\n // ─── Build weapons ───\n console.log(\"Converting weapons...\");\n const { weapons, unitWeaponIds } = buildWeaponRegistry(\n unitWargearMap,\n GAME_VERSION\n );\n\n // Wire weapon_ids into units\n for (const unit of units) {\n const dsId = factionDatasheets.find((d) => d.name === (unit as { name: string }).name)!.id;\n const weaponIds = unitWeaponIds.get(dsId);\n if (weaponIds) {\n (unit as { weapon_ids: string[] }).weapon_ids = [...weaponIds].sort();\n }\n }\n\n // ─── Build leader attachments ───\n console.log(\"Converting leader attachments...\");\n const leaderMap = new Map<string, Set<string>>();\n\n for (const l of allLeaders) {\n if (factionIds.has(l.leader_id) && factionIds.has(l.attached_id)) {\n const leaderId = nameToId(idToName.get(l.leader_id)!);\n const attachedId = nameToId(idToName.get(l.attached_id)!);\n if (!leaderMap.has(leaderId)) {\n leaderMap.set(leaderId, new Set());\n }\n leaderMap.get(leaderId)!.add(attachedId);\n }\n }\n\n const leaderAttachments = [...leaderMap.entries()].map(\n ([leaderId, bodyguards]) => ({\n leader_id: leaderId,\n eligible_bodyguard_ids: [...bodyguards].sort(),\n max_leaders_per_unit: 1,\n game_version: GAME_VERSION,\n })\n );\n // ─── Build detachments ───\n console.log(\"Converting detachments...\");\n const factionDetAbilities = detachmentAbilities.filter(\n (d) => d.faction_id === sourceFactionId\n );\n let factionEnhancements = enhancements.filter(\n (e) => e.faction_id === sourceFactionId\n );\n let factionStratagems = stratagems.filter(\n (s) => s.faction_id === sourceFactionId\n );\n\n // Deduplicate detachments (some have multiple ability entries per detachment)\n let detachmentNames = [...new Set(factionDetAbilities.map((da) => da.detachment))];\n\n // Apply detachment filter for subfactions\n if (config.detachmentFilter) {\n detachmentNames = detachmentNames.filter((d) =>\n config.detachmentFilter!.includes(d)\n );\n // Also filter enhancements and stratagems to matching detachments\n const allowedDetachments = new Set(config.detachmentFilter);\n factionEnhancements = factionEnhancements.filter((e) => allowedDetachments.has(e.detachment));\n factionStratagems = factionStratagems.filter((s) => allowedDetachments.has(s.detachment));\n }\n\n const detachments = detachmentNames.map((detName) => {\n const detId = nameToId(detName);\n const detEnhIds = factionEnhancements\n .filter((e) => e.detachment === detName)\n .map((e) => nameToId(e.name));\n const detStratIds = factionStratagems\n .filter((s) => s.detachment === detName)\n .map((s) => nameToId(s.name));\n\n return {\n id: detId,\n name: detName,\n faction_id: factionId,\n detachment_rule_id: null as string | null,\n enhancement_ids: detEnhIds,\n stratagem_ids: detStratIds,\n game_version: GAME_VERSION,\n };\n });\n\n // ─── Build enhancements ───\n console.log(\"Converting enhancements...\");\n const enhancementEntities = factionEnhancements.map((e) => ({\n id: nameToId(e.name),\n name: e.name,\n detachment_id: nameToId(e.detachment),\n cost: parseInt(e.cost, 10),\n keyword_restrictions: [factionName],\n ability_id: null as string | null,\n is_unique: true,\n game_version: GAME_VERSION,\n }));\n\n // ─── Build stratagems ───\n console.log(\"Converting stratagems...\");\n const stratagemEntities = factionStratagems.map((s) => {\n const { type } = parseStratagemType(s.type);\n const phases = mapPhases(s.phases);\n const playerTurn = parsePlayerTurn(s.turn);\n\n return {\n id: nameToId(s.name),\n name: s.name,\n category: \"detachment\" as const,\n type,\n detachment_id: nameToId(s.detachment),\n cp_cost: parseInt(s.cp_cost, 10),\n phases,\n player_turn: playerTurn,\n timing: \"once-per-phase\" as const,\n target_restrictions: null as null,\n ability_id: null as string | null,\n game_version: GAME_VERSION,\n };\n });\n\n // ─── Build phase mappings from source ability phases ───\n console.log(\"Converting phase mappings...\");\n const phaseMappings: Record<string, unknown>[] = [];\n\n // Unit abilities\n for (const ds of factionDatasheets) {\n const viewIdx = viewIndices.get(ds.id)!;\n const dsAbilities = allAbilities.filter(\n (a) => a.datasheet_id === ds.id\n );\n const viewAbilities = getViewEntries(dsAbilities, viewIdx);\n const seen = new Set<string>();\n\n for (const a of viewAbilities) {\n if (a.type === \"Faction\") continue;\n const sourceId = nameToId(a.name);\n if (seen.has(sourceId)) continue;\n seen.add(sourceId);\n\n const sourceType =\n a.type === \"Core\" ? \"ability\" :\n a.type === \"Wargear\" ? \"ability\" :\n \"ability\";\n\n const phases = mapPhases(a.phases);\n if (phases.length > 0) {\n phaseMappings.push({\n source_id: sourceId,\n source_type: sourceType,\n phases,\n game_version: GAME_VERSION,\n authored_by: \"40kdc-community\",\n });\n }\n }\n }\n\n // Stratagem phase mappings\n for (const s of factionStratagems) {\n const phases = mapPhases(s.phases);\n if (phases.length > 0) {\n phaseMappings.push({\n source_id: nameToId(s.name),\n source_type: \"stratagem\",\n phases,\n game_version: GAME_VERSION,\n authored_by: \"40kdc-community\",\n });\n }\n }\n\n // Enhancement phase mappings\n for (const e of factionEnhancements) {\n const phases = mapPhases(e.phases);\n if (phases.length > 0) {\n phaseMappings.push({\n source_id: nameToId(e.name),\n source_type: \"enhancement\",\n phases,\n game_version: GAME_VERSION,\n authored_by: \"40kdc-community\",\n });\n }\n }\n\n // Detachment rule phase mappings\n for (const da of factionDetAbilities) {\n const phases = mapPhases(da.phases);\n if (phases.length > 0) {\n phaseMappings.push({\n source_id: nameToId(da.name),\n source_type: \"detachment-rule\",\n phases,\n game_version: GAME_VERSION,\n authored_by: \"40kdc-community\",\n });\n }\n }\n\n // Deduplicate phase mappings\n const dedupedPhaseMappings = [\n ...new Map(\n phaseMappings.map((pm) => [\n `${(pm as { source_id: string }).source_id}|${(pm as { source_type: string }).source_type}`,\n pm,\n ])\n ).values(),\n ];\n\n // ─── Build unit compositions ───\n console.log(\"Generating unit compositions...\");\n\n const unitCompositions = units.map((u) => {\n const unitId = (u as { id: string }).id;\n const modelCount = (u as { model_count: { min: number; max: number } }).model_count;\n\n const override = config.compositionOverrides[unitId];\n if (override) {\n return {\n unit_id: unitId,\n models: override,\n game_version: GAME_VERSION,\n };\n }\n\n // Single-model unit (vehicles, characters, monsters)\n const profileName = (u as { profiles: { name: string }[] }).profiles[0]?.name;\n return {\n unit_id: unitId,\n models: [\n {\n name: profileName || (u as { name: string }).name,\n min: modelCount.min,\n max: modelCount.max,\n is_leader_model: false,\n },\n ],\n game_version: GAME_VERSION,\n };\n });\n\n // ─── Generate factions.json ───\n const factionEntity = [\n {\n id: factionId,\n name: factionName,\n parent_faction_id: config.parentFactionId,\n game_version: GAME_VERSION,\n keywords: config.factionKeywords,\n aliases: config.aliases,\n faction_rule_id: config.factionRuleId,\n },\n ];\n\n // ─── Write output ──────────────────────────────────────────────────\n const coreDir = `data/core/${factionId}`;\n const enrichDir = `data/enrichment/${factionId}`;\n mkdirSync(resolve(ROOT, coreDir), { recursive: true });\n mkdirSync(resolve(ROOT, enrichDir), { recursive: true });\n\n console.log(\"\\nWriting output files...\");\n\n writeOutput(`${coreDir}/factions.json`, factionEntity);\n if (!config.skipUnits) {\n writeOutput(`${coreDir}/units.json`, units);\n writeOutput(`${coreDir}/weapons.json`, weapons);\n writeOutput(`${coreDir}/leader-attachments.json`, leaderAttachments);\n writeOutput(`${coreDir}/unit-compositions.json`, unitCompositions);\n }\n writeOutput(`${coreDir}/detachments.json`, detachments);\n writeOutput(`${coreDir}/enhancements.json`, enhancementEntities);\n writeOutput(`${coreDir}/stratagems.json`, stratagemEntities);\n if (!config.skipUnits) {\n writeOutput(`${enrichDir}/phase-mappings.json`, dedupedPhaseMappings);\n }\n\n // ─── Summary ───\n console.log(`\\n── ${factionName} Summary ──`);\n if (!config.skipUnits) {\n console.log(` Units: ${units.length}`);\n console.log(` Weapons: ${weapons.length}`);\n console.log(` Leader attachments: ${leaderAttachments.length}`);\n console.log(` Unit compositions: ${unitCompositions.length}`);\n }\n console.log(` Detachments: ${detachments.length}`);\n console.log(` Enhancements: ${enhancementEntities.length}`);\n console.log(` Stratagems: ${stratagemEntities.length}`);\n if (!config.skipUnits) {\n console.log(` Phase mappings: ${dedupedPhaseMappings.length}`);\n }\n console.log(\"\\nDone. Run 'npm run validate' to check output.\");\n}\n\n// ─── CLI entry point ─────────────────────────────────────────────────\n\n// Only run CLI when this module is the entry point (not when imported)\nconst isMain = process.argv[1] &&\n resolve(process.argv[1]).replace(/\\.\\w+$/, \"\") ===\n fileURLToPath(import.meta.url).replace(/\\.\\w+$/, \"\");\n\nif (isMain) {\n const args = process.argv.slice(2);\n\n if (args.length === 0 || args[0] === \"--help\") {\n console.log(\"Usage: npx tsx tools/src/convert-faction.ts <faction-id>\");\n console.log(`Available factions: ${listFactions().join(\", \")}`);\n process.exit(args[0] === \"--help\" ? 0 : 1);\n }\n\n const factionIdArg = args[0];\n const factionConfig = getFactionConfig(factionIdArg);\n convertFaction(factionConfig);\n}\n"]}
1
+ {"version":3,"file":"convert-faction.js","sourceRoot":"","sources":["../src/convert-faction.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Generic faction converter: army-assist → 40kdc-data format.\n *\n * Usage: npx tsx tools/src/convert-faction.ts <faction-id>\n * Example: npx tsx tools/src/convert-faction.ts emperors-children\n *\n * Faction configs are registered via side-effect imports from ./converters/configs/.\n */\n\nimport { readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { resolve, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { nameToId, parseStratagemType, parsePlayerTurn, mapPhases } from \"./converters/id-generator.js\";\nimport { parseMove, parseTargetNumber, parseIntStat, parseInvuln } from \"./converters/stat-parser.js\";\nimport { findFactionViewIndex, getViewEntries, getPointsForView, splitIntoViews, type SourceAbility } from \"./converters/view-selector.js\";\nimport { buildWeaponRegistry, type SourceWargear } from \"./converters/weapon-dedup.js\";\nimport { buildWargearOptions } from \"./converters/wargear-options.js\";\nimport { getKeywordsForFaction, type SourceKeyword } from \"./converters/keyword-filter.js\";\nimport { type FactionConfig, getFactionConfig, listFactions } from \"./converters/faction-config.js\";\n\n// Register all faction configs\nimport \"./converters/configs/world-eaters.js\";\nimport \"./converters/configs/emperors-children.js\";\nimport \"./converters/configs/chaos-knights.js\";\nimport \"./converters/configs/imperial-knights.js\";\nimport \"./converters/configs/leagues-of-votann.js\";\nimport \"./converters/configs/drukhari.js\";\nimport \"./converters/configs/genestealer-cults.js\";\nimport \"./converters/configs/grey-knights.js\";\nimport \"./converters/configs/thousand-sons.js\";\nimport \"./converters/configs/death-guard.js\";\nimport \"./converters/configs/adeptus-custodes.js\";\nimport \"./converters/configs/adepta-sororitas.js\";\nimport \"./converters/configs/agents-of-the-imperium.js\";\nimport \"./converters/configs/adeptus-mechanicus.js\";\nimport \"./converters/configs/tau-empire.js\";\nimport \"./converters/configs/tyranids.js\";\nimport \"./converters/configs/necrons.js\";\nimport \"./converters/configs/chaos-daemons.js\";\nimport \"./converters/configs/orks.js\";\nimport \"./converters/configs/aeldari.js\";\nimport \"./converters/configs/chaos-space-marines.js\";\nimport \"./converters/configs/astra-militarum.js\";\nimport \"./converters/configs/adeptus-astartes.js\";\nimport \"./converters/configs/blood-angels.js\";\nimport \"./converters/configs/dark-angels.js\";\nimport \"./converters/configs/space-wolves.js\";\nimport \"./converters/configs/black-templars.js\";\nimport \"./converters/configs/deathwatch.js\";\nimport \"./converters/configs/ultramarines.js\";\nimport \"./converters/configs/imperial-fists.js\";\nimport \"./converters/configs/crimson-fists.js\";\nimport \"./converters/configs/iron-hands.js\";\nimport \"./converters/configs/raven-guard.js\";\nimport \"./converters/configs/salamanders.js\";\nimport \"./converters/configs/white-scars.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst ROOT = resolve(__dirname, \"../..\");\nconst SOURCE = resolve(process.env.HOME!, \"army-assist/src/assets/json\");\nconst GAME_VERSION = { edition: \"10th\", dataslate: \"2025-q3\" };\n\n// ─── Source data types ───────────────────────────────────────────────\n\ninterface SourceDatasheet {\n id: string;\n name: string;\n faction_id: string;\n loadout: string;\n transport: string;\n role: string;\n damaged_w: string;\n damaged_description: string;\n}\n\ninterface SourceModel {\n datasheet_id: string;\n line: string;\n name: string;\n M: string;\n T: string;\n Sv: string;\n inv_sv: string;\n inv_sv_descr: string;\n W: string;\n Ld: string;\n OC: string;\n base_size: string;\n base_size_descr: string;\n}\n\ninterface SourcePoints {\n datasheet_id: string;\n models: string;\n cost: string;\n}\n\ninterface SourceLeader {\n leader_id: string;\n attached_id: string;\n}\n\ninterface SourceEnhancement {\n id: string;\n name: string;\n faction_id: string;\n cost: string;\n detachment: string;\n phases: string[];\n}\n\ninterface SourceStratagem {\n id: string;\n name: string;\n faction_id: string;\n type: string;\n cp_cost: string;\n turn: string;\n phase: string;\n detachment: string;\n phases: string[];\n}\n\ninterface SourceDetachmentAbility {\n name: string;\n faction_id: string;\n detachment: string;\n phases: string[];\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────\n\nfunction readJSON<T>(filename: string): T {\n return JSON.parse(readFileSync(resolve(SOURCE, filename), \"utf-8\"));\n}\n\nfunction writeOutput(relPath: string, data: unknown): void {\n const outPath = resolve(ROOT, relPath);\n writeFileSync(outPath, JSON.stringify(data, null, 2) + \"\\n\");\n console.log(` ✓ ${relPath} (${Array.isArray(data) ? data.length : 1} entries)`);\n}\n\n/** Determine unit role from keywords and abilities. */\nfunction deriveRole(\n keywords: string[],\n abilities: SourceAbility[],\n name: string\n): string | undefined {\n const kw = new Set(keywords.map((k) => k.toLowerCase()));\n if (kw.has(\"epic hero\")) return \"epic-hero\";\n\n if (kw.has(\"character\")) return \"character\";\n const hasLeader = abilities.some(\n (a) => a.type === \"Core\" && a.name === \"Leader\"\n );\n if (hasLeader) return \"character\";\n\n if (kw.has(\"battleline\")) return \"battleline\";\n\n if (\n name.toLowerCase().includes(\"rhino\") ||\n kw.has(\"dedicated transport\")\n )\n return \"dedicated-transport\";\n\n return undefined;\n}\n\n/** Parse base size string. \"32mm\" → { shape: \"round\", diameter: 32 } */\nfunction parseBaseSize(\n s: string\n): { shape: string; diameter?: number; width?: number; length?: number } | undefined {\n if (!s || s.trim() === \"\") return undefined;\n const round = s.match(/(\\d+)\\s*mm/i);\n if (round) return { shape: \"round\", diameter: parseInt(round[1], 10) };\n const oval = s.match(/(\\d+)\\s*x\\s*(\\d+)/i);\n if (oval)\n return {\n shape: \"oval\",\n width: parseInt(oval[1], 10),\n length: parseInt(oval[2], 10),\n };\n return undefined;\n}\n\n/** Parse transport capacity from transport text. */\nfunction parseTransport(\n s: string\n): { capacity: number; keyword_restrictions?: string[]; exclusion_keywords?: string[] } | undefined {\n if (!s || s.trim() === \"\") return undefined;\n // Match \"capacity of N\" or \"N <FACTION> INFANTRY\"\n const capMatch = s.match(/capacity\\s*(?:of\\s*)?(\\d+)/i) || s.match(/(\\d+)\\s+\\S[\\S\\s]*?\\s+(?:infantry|model)/i);\n if (!capMatch) return undefined;\n const capacity = parseInt(capMatch[1], 10);\n const result: { capacity: number; keyword_restrictions?: string[]; exclusion_keywords?: string[] } = { capacity };\n\n if (/jump pack/i.test(s) && /cannot/i.test(s)) {\n result.exclusion_keywords = [\"Jump Pack\"];\n }\n return result;\n}\n\n// ─── Main conversion ─────────────────────────────────────────────────\n\nexport function convertFaction(\n config: FactionConfig,\n options: { wargearOnly?: boolean } = {},\n): void {\n const { sourceFactionId, factionId, factionName, factionAbilityName } = config;\n\n console.log(`Converting ${factionName} (${sourceFactionId} → ${factionId})...`);\n console.log(\"Loading source data from army-assist...\");\n\n const datasheets = readJSON<SourceDatasheet[]>(\"Datasheets.json\");\n const allModels = readJSON<SourceModel[]>(\"Datasheets_models.json\");\n const allWargear = readJSON<SourceWargear[]>(\"Datasheets_wargear.json\");\n const allOptions = readJSON<\n { datasheet_id: string; line: string; description: string | null }[]\n >(\"Datasheets_options.json\");\n const allComposition = readJSON<\n { datasheet_id: string; line: string; description: string }[]\n >(\"Datasheets_unit_composition.json\");\n const allAbilities = readJSON<SourceAbility[]>(\"Datasheets_abilities.json\");\n const allKeywords = readJSON<SourceKeyword[]>(\"Datasheets_keywords.json\");\n const allPoints = readJSON<SourcePoints[]>(\"Datasheets_points.json\");\n const allLeaders = readJSON<SourceLeader[]>(\"Datasheets_leader.json\");\n const enhancements = readJSON<SourceEnhancement[]>(\"Enhancements.json\");\n const stratagems = readJSON<SourceStratagem[]>(\"Stratagems.json\");\n const detachmentAbilities = readJSON<SourceDetachmentAbility[]>(\n \"Detachment_abilities.json\"\n );\n\n // Filter to target faction, skip datasheets with no model data (metadata entries)\n const modelDatasheetIds = new Set(allModels.map((m) => m.datasheet_id));\n const factionDatasheets = datasheets.filter(\n (d) => d.faction_id === sourceFactionId && modelDatasheetIds.has(d.id)\n );\n const factionIds = new Set(factionDatasheets.map((d) => d.id));\n const idToName = new Map(factionDatasheets.map((d) => [d.id, d.name]));\n\n console.log(`Found ${factionDatasheets.length} ${factionName} datasheets\\n`);\n\n // ─── Determine view indices for shared units ───\n const viewIndices = new Map<string, number>();\n for (const ds of factionDatasheets) {\n const dsAbilities = allAbilities.filter(\n (a) => a.datasheet_id === ds.id\n );\n viewIndices.set(ds.id, findFactionViewIndex(dsAbilities, factionAbilityName));\n }\n\n // ─── Build units ───\n console.log(\"Converting units...\");\n const units: Record<string, unknown>[] = [];\n\n const unitWargearMap = new Map<string, SourceWargear[]>();\n const unitAbilityNames = new Map<string, string[]>();\n\n for (const ds of factionDatasheets) {\n const viewIdx = viewIndices.get(ds.id)!;\n\n // Models\n const dsModels = allModels.filter((m) => m.datasheet_id === ds.id);\n const viewModels = getViewEntries(dsModels, viewIdx);\n\n // Abilities (for role derivation and ability name collection)\n const dsAbilities = allAbilities.filter(\n (a) => a.datasheet_id === ds.id\n );\n const viewAbilities = getViewEntries(dsAbilities, viewIdx);\n\n const abilityNames = viewAbilities\n .filter((a) => a.type !== \"Faction\")\n .map((a) => a.name);\n unitAbilityNames.set(ds.id, [...new Set(abilityNames)]);\n\n // Keywords — use faction-aware filtering for shared units\n const dsKeywords = allKeywords.filter(\n (k) => k.datasheet_id === ds.id\n );\n const { factionKeywords, regularKeywords } = getKeywordsForFaction(\n dsKeywords,\n factionName\n );\n\n // Points — select the correct view for shared units\n const allDsPoints = allPoints.filter((p) => p.datasheet_id === ds.id);\n const dsAbilityViews = splitIntoViews(\n allAbilities.filter((a) => a.datasheet_id === ds.id)\n );\n const numViews = dsAbilityViews.length;\n const viewPoints = getPointsForView(allDsPoints, viewIdx, numViews);\n const dsPoints = viewPoints\n .map((p) => ({\n models: parseInt(p.models, 10),\n cost: parseInt(p.cost, 10),\n }))\n .sort((a, b) => a.models - b.models);\n\n // Wargear for this unit's view\n const dsWargear = allWargear.filter(\n (w) => w.datasheet_id === ds.id\n );\n const viewWargear = getViewEntries(dsWargear, viewIdx);\n unitWargearMap.set(ds.id, viewWargear);\n\n // Build stat profiles\n const profiles = viewModels.map((m) => {\n const profile: Record<string, unknown> = {\n name: m.name,\n M: parseMove(m.M),\n T: parseIntStat(m.T),\n W: parseIntStat(m.W),\n Sv: parseTargetNumber(m.Sv)!,\n invuln_sv: parseInvuln(m.inv_sv),\n Ld: parseTargetNumber(m.Ld)!,\n OC: parseIntStat(m.OC),\n };\n return profile;\n });\n\n const role = deriveRole(regularKeywords, viewAbilities, ds.name);\n const baseSize = parseBaseSize(\n viewModels[0]?.base_size ?? \"\"\n );\n const transport = parseTransport(ds.transport);\n\n const modelMin = dsPoints.length > 0 ? dsPoints[0].models : 1;\n const modelMax =\n dsPoints.length > 0 ? dsPoints[dsPoints.length - 1].models : 1;\n\n const unitId = nameToId(ds.name);\n const unit: Record<string, unknown> = {\n id: unitId,\n name: ds.name,\n faction_id: factionId,\n ...(role ? { role } : {}),\n profiles,\n points: dsPoints,\n keywords: regularKeywords,\n faction_keywords: factionKeywords,\n ...(baseSize ? { base_size_mm: baseSize } : {}),\n model_count: { min: modelMin, max: modelMax },\n weapon_ids: [],\n ability_ids: [],\n ...(transport ? { transport_capacity: transport } : {}),\n game_version: GAME_VERSION,\n is_legend: false,\n };\n\n units.push(unit);\n }\n\n // ─── Build weapons ───\n console.log(\"Converting weapons...\");\n const { weapons, unitWeaponIds } = buildWeaponRegistry(\n unitWargearMap,\n GAME_VERSION\n );\n\n // Wire weapon_ids into units\n for (const unit of units) {\n const dsId = factionDatasheets.find((d) => d.name === (unit as { name: string }).name)!.id;\n const weaponIds = unitWeaponIds.get(dsId);\n if (weaponIds) {\n (unit as { weapon_ids: string[] }).weapon_ids = [...weaponIds].sort();\n }\n }\n\n // ─── Build wargear options + non-weapon wargear ───\n console.log(\"Converting wargear options...\");\n const globalWeaponIds = new Set(weapons.map((w) => w.id));\n const {\n wargearOptions,\n wargear: wargearItems,\n unparsed: unparsedOptions,\n } = buildWargearOptions(\n factionDatasheets.map((d) => ({ id: d.id, name: d.name })),\n allModels,\n allOptions,\n allComposition,\n unitWeaponIds,\n globalWeaponIds,\n GAME_VERSION\n );\n\n // ─── Build leader attachments ───\n console.log(\"Converting leader attachments...\");\n const leaderMap = new Map<string, Set<string>>();\n\n for (const l of allLeaders) {\n if (factionIds.has(l.leader_id) && factionIds.has(l.attached_id)) {\n const leaderId = nameToId(idToName.get(l.leader_id)!);\n const attachedId = nameToId(idToName.get(l.attached_id)!);\n if (!leaderMap.has(leaderId)) {\n leaderMap.set(leaderId, new Set());\n }\n leaderMap.get(leaderId)!.add(attachedId);\n }\n }\n\n const leaderAttachments = [...leaderMap.entries()].map(\n ([leaderId, bodyguards]) => ({\n leader_id: leaderId,\n eligible_bodyguard_ids: [...bodyguards].sort(),\n max_leaders_per_unit: 1,\n game_version: GAME_VERSION,\n })\n );\n // ─── Build detachments ───\n console.log(\"Converting detachments...\");\n const factionDetAbilities = detachmentAbilities.filter(\n (d) => d.faction_id === sourceFactionId\n );\n let factionEnhancements = enhancements.filter(\n (e) => e.faction_id === sourceFactionId\n );\n let factionStratagems = stratagems.filter(\n (s) => s.faction_id === sourceFactionId\n );\n\n // Deduplicate detachments (some have multiple ability entries per detachment)\n let detachmentNames = [...new Set(factionDetAbilities.map((da) => da.detachment))];\n\n // Apply detachment filter for subfactions\n if (config.detachmentFilter) {\n detachmentNames = detachmentNames.filter((d) =>\n config.detachmentFilter!.includes(d)\n );\n // Also filter enhancements and stratagems to matching detachments\n const allowedDetachments = new Set(config.detachmentFilter);\n factionEnhancements = factionEnhancements.filter((e) => allowedDetachments.has(e.detachment));\n factionStratagems = factionStratagems.filter((s) => allowedDetachments.has(s.detachment));\n }\n\n const detachments = detachmentNames.map((detName) => {\n const detId = nameToId(detName);\n const detEnhIds = factionEnhancements\n .filter((e) => e.detachment === detName)\n .map((e) => nameToId(e.name));\n const detStratIds = factionStratagems\n .filter((s) => s.detachment === detName)\n .map((s) => nameToId(s.name));\n\n return {\n id: detId,\n name: detName,\n faction_id: factionId,\n detachment_rule_id: null as string | null,\n enhancement_ids: detEnhIds,\n stratagem_ids: detStratIds,\n game_version: GAME_VERSION,\n };\n });\n\n // ─── Build enhancements ───\n console.log(\"Converting enhancements...\");\n const enhancementEntities = factionEnhancements.map((e) => ({\n id: nameToId(e.name),\n name: e.name,\n detachment_id: nameToId(e.detachment),\n cost: parseInt(e.cost, 10),\n keyword_restrictions: [factionName],\n ability_id: null as string | null,\n is_unique: true,\n game_version: GAME_VERSION,\n }));\n\n // ─── Build stratagems ───\n console.log(\"Converting stratagems...\");\n const stratagemEntities = factionStratagems.map((s) => {\n const { type } = parseStratagemType(s.type);\n const phases = mapPhases(s.phases);\n const playerTurn = parsePlayerTurn(s.turn);\n\n return {\n id: nameToId(s.name),\n name: s.name,\n category: \"detachment\" as const,\n type,\n detachment_id: nameToId(s.detachment),\n cp_cost: parseInt(s.cp_cost, 10),\n phases,\n player_turn: playerTurn,\n timing: \"once-per-phase\" as const,\n target_restrictions: null as null,\n ability_id: null as string | null,\n game_version: GAME_VERSION,\n };\n });\n\n // ─── Build phase mappings from source ability phases ───\n console.log(\"Converting phase mappings...\");\n const phaseMappings: Record<string, unknown>[] = [];\n\n // Unit abilities\n for (const ds of factionDatasheets) {\n const viewIdx = viewIndices.get(ds.id)!;\n const dsAbilities = allAbilities.filter(\n (a) => a.datasheet_id === ds.id\n );\n const viewAbilities = getViewEntries(dsAbilities, viewIdx);\n const seen = new Set<string>();\n\n for (const a of viewAbilities) {\n if (a.type === \"Faction\") continue;\n const sourceId = nameToId(a.name);\n if (seen.has(sourceId)) continue;\n seen.add(sourceId);\n\n const sourceType =\n a.type === \"Core\" ? \"ability\" :\n a.type === \"Wargear\" ? \"ability\" :\n \"ability\";\n\n const phases = mapPhases(a.phases);\n if (phases.length > 0) {\n phaseMappings.push({\n source_id: sourceId,\n source_type: sourceType,\n phases,\n game_version: GAME_VERSION,\n authored_by: \"40kdc-community\",\n });\n }\n }\n }\n\n // Stratagem phase mappings\n for (const s of factionStratagems) {\n const phases = mapPhases(s.phases);\n if (phases.length > 0) {\n phaseMappings.push({\n source_id: nameToId(s.name),\n source_type: \"stratagem\",\n phases,\n game_version: GAME_VERSION,\n authored_by: \"40kdc-community\",\n });\n }\n }\n\n // Enhancement phase mappings\n for (const e of factionEnhancements) {\n const phases = mapPhases(e.phases);\n if (phases.length > 0) {\n phaseMappings.push({\n source_id: nameToId(e.name),\n source_type: \"enhancement\",\n phases,\n game_version: GAME_VERSION,\n authored_by: \"40kdc-community\",\n });\n }\n }\n\n // Detachment rule phase mappings\n for (const da of factionDetAbilities) {\n const phases = mapPhases(da.phases);\n if (phases.length > 0) {\n phaseMappings.push({\n source_id: nameToId(da.name),\n source_type: \"detachment-rule\",\n phases,\n game_version: GAME_VERSION,\n authored_by: \"40kdc-community\",\n });\n }\n }\n\n // Deduplicate phase mappings\n const dedupedPhaseMappings = [\n ...new Map(\n phaseMappings.map((pm) => [\n `${(pm as { source_id: string }).source_id}|${(pm as { source_type: string }).source_type}`,\n pm,\n ])\n ).values(),\n ];\n\n // ─── Build unit compositions ───\n console.log(\"Generating unit compositions...\");\n\n const unitCompositions = units.map((u) => {\n const unitId = (u as { id: string }).id;\n const modelCount = (u as { model_count: { min: number; max: number } }).model_count;\n\n const override = config.compositionOverrides[unitId];\n if (override) {\n return {\n unit_id: unitId,\n models: override,\n game_version: GAME_VERSION,\n };\n }\n\n // Single-model unit (vehicles, characters, monsters)\n const profileName = (u as { profiles: { name: string }[] }).profiles[0]?.name;\n return {\n unit_id: unitId,\n models: [\n {\n name: profileName || (u as { name: string }).name,\n min: modelCount.min,\n max: modelCount.max,\n is_leader_model: false,\n },\n ],\n game_version: GAME_VERSION,\n };\n });\n\n // ─── Generate factions.json ───\n const factionEntity = [\n {\n id: factionId,\n name: factionName,\n parent_faction_id: config.parentFactionId,\n game_version: GAME_VERSION,\n keywords: config.factionKeywords,\n aliases: config.aliases,\n faction_rule_id: config.factionRuleId,\n },\n ];\n\n // ─── Write output ──────────────────────────────────────────────────\n const coreDir = `data/core/${factionId}`;\n const enrichDir = `data/enrichment/${factionId}`;\n mkdirSync(resolve(ROOT, coreDir), { recursive: true });\n mkdirSync(resolve(ROOT, enrichDir), { recursive: true });\n\n console.log(\"\\nWriting output files...\");\n\n // `wargearOnly` adds just the wargear data: the rest of the converter's output\n // has drifted from the committed dataset (e.g. weapon keywords are object refs\n // there, strings here), so a full rewrite would regress those files. Adding\n // wargear must not touch them.\n if (!options.wargearOnly) {\n writeOutput(`${coreDir}/factions.json`, factionEntity);\n }\n if (!config.skipUnits) {\n if (!options.wargearOnly) {\n writeOutput(`${coreDir}/units.json`, units);\n writeOutput(`${coreDir}/weapons.json`, weapons);\n writeOutput(`${coreDir}/leader-attachments.json`, leaderAttachments);\n writeOutput(`${coreDir}/unit-compositions.json`, unitCompositions);\n }\n writeOutput(`${coreDir}/wargear-options.json`, wargearOptions);\n if (wargearItems.length > 0) {\n writeOutput(`${coreDir}/wargear.json`, wargearItems);\n }\n if (unparsedOptions.length > 0) {\n // Underscore-prefixed: a report for manual review, skipped by validation\n // and not bundled into the dataset.\n writeOutput(`${coreDir}/_wargear-options.unparsed.json`, unparsedOptions);\n }\n }\n if (!options.wargearOnly) {\n writeOutput(`${coreDir}/detachments.json`, detachments);\n writeOutput(`${coreDir}/enhancements.json`, enhancementEntities);\n writeOutput(`${coreDir}/stratagems.json`, stratagemEntities);\n if (!config.skipUnits) {\n writeOutput(`${enrichDir}/phase-mappings.json`, dedupedPhaseMappings);\n }\n }\n\n // ─── Summary ───\n console.log(`\\n── ${factionName} Summary ──`);\n if (!config.skipUnits) {\n console.log(` Units: ${units.length}`);\n console.log(` Weapons: ${weapons.length}`);\n console.log(` Leader attachments: ${leaderAttachments.length}`);\n console.log(` Unit compositions: ${unitCompositions.length}`);\n console.log(` Wargear options: ${wargearOptions.length}`);\n console.log(` Wargear items: ${wargearItems.length}`);\n console.log(` Unparsed options: ${unparsedOptions.length}`);\n }\n console.log(` Detachments: ${detachments.length}`);\n console.log(` Enhancements: ${enhancementEntities.length}`);\n console.log(` Stratagems: ${stratagemEntities.length}`);\n if (!config.skipUnits) {\n console.log(` Phase mappings: ${dedupedPhaseMappings.length}`);\n }\n console.log(\"\\nDone. Run 'npm run validate' to check output.\");\n}\n\n// ─── CLI entry point ─────────────────────────────────────────────────\n\n// Only run CLI when this module is the entry point (not when imported)\nconst isMain = process.argv[1] &&\n resolve(process.argv[1]).replace(/\\.\\w+$/, \"\") ===\n fileURLToPath(import.meta.url).replace(/\\.\\w+$/, \"\");\n\nif (isMain) {\n const args = process.argv.slice(2);\n const wargearOnly = args.includes(\"--wargear-only\");\n const positional = args.filter((a) => !a.startsWith(\"--\"));\n\n if (positional.length === 0 || args[0] === \"--help\") {\n console.log(\n \"Usage: npx tsx tools/src/convert-faction.ts <faction-id|all> [--wargear-only]\",\n );\n console.log(`Available factions: ${listFactions().join(\", \")}`);\n process.exit(args[0] === \"--help\" ? 0 : 1);\n }\n\n const target = positional[0];\n const factionIds = target === \"all\" ? listFactions() : [target];\n for (const id of factionIds) {\n convertFaction(getFactionConfig(id), { wargearOnly });\n }\n}\n"]}
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Bridge GW base-size data onto units and their per-model compositions.
3
+ *
4
+ * Two sources, in priority order:
5
+ * 1. The GW *Chapter Approved Tournament Companion — Base Size Guide* (committed
6
+ * as numerical-facts rows in `data/base-size-guide.json`). Authoritative for
7
+ * current matched-play units. Multi-model datasheets list each model as a
8
+ * "Unit: ModelLabel" row; the bare "Unit" row is the default for unlisted models.
9
+ * 2. bevy-deploy-helper's per-model table, used only as a fallback for the Forge
10
+ * World / Legends units the tournament guide omits.
11
+ *
12
+ * The match key is the folded datasheet name — 40kdc unit ids are themselves
13
+ * generated from GW names (see `id-generator.ts`), so a direct fold joins exactly.
14
+ * Per-model bases attach to the existing `unit-composition` model entries; the
15
+ * unit-level `base_size_mm` is the representative (most-numerous) model's base.
16
+ *
17
+ * Nothing here touches the filesystem — see `commands/populate-base-sizes.ts`.
18
+ */
19
+ /** A resolved base size, shaped to the `base-size` schema def. */
20
+ export interface BaseSize {
21
+ shape: "round" | "oval" | "flying-base" | "hull" | "unique";
22
+ diameter?: number;
23
+ width?: number;
24
+ length?: number;
25
+ size?: "small" | "large";
26
+ /** Provisional/guessed (a category without authoritative dims). Omitted when false. */
27
+ draft?: true;
28
+ }
29
+ /** Fold a display name to the same kebab key `nameToId` produces, but never throws. */
30
+ export declare function foldName(s: string): string;
31
+ export type ParseResult = {
32
+ ok: true;
33
+ base: BaseSize;
34
+ } | {
35
+ ok: false;
36
+ reason: "empty" | "unparseable";
37
+ };
38
+ /**
39
+ * Parse a raw base-size string. Tolerant of curly quotes, `×`, decimals, spacing
40
+ * and the "Oval Base" suffix. Categories the guide gives without authoritative
41
+ * millimetres (`Hull`, `Unique`, `Small Flying Base`) parse to draft entries;
42
+ * `Large Flying Base` additionally gets its well-attested 120×92mm oval.
43
+ */
44
+ export declare function parseBaseSize(raw: string): ParseResult;
45
+ export interface GuideRow {
46
+ unit: string;
47
+ model?: string;
48
+ raw: string;
49
+ }
50
+ export interface GuideEntry {
51
+ /** Base from the bare "Unit" row, applied to models without an override. */
52
+ default?: BaseSize;
53
+ /** Per-model overrides, keyed by folded model label. */
54
+ overrides: Map<string, BaseSize>;
55
+ }
56
+ export interface GuideIndex {
57
+ byUnit: Map<string, GuideEntry>;
58
+ /** Rows whose size string failed to parse (should be empty for guide data). */
59
+ unparsed: GuideRow[];
60
+ }
61
+ /** Build the guide index from committed rows. Splits "A/B" shared rows on "/". */
62
+ export declare function buildGuideIndex(rows: readonly GuideRow[]): GuideIndex;
63
+ export interface BevySources {
64
+ /** bevy Datasheets.json: [{ id, name }]. */
65
+ datasheets: ReadonlyArray<{
66
+ id: string;
67
+ name: string;
68
+ }>;
69
+ /** bevy Datasheets_models.json: [{ datasheet_id, base_size }]. */
70
+ models: ReadonlyArray<{
71
+ datasheet_id: string;
72
+ base_size?: string;
73
+ }>;
74
+ }
75
+ /**
76
+ * Build a unit-id → base map from bevy, keeping only datasheets whose model rows
77
+ * agree on a single base. Disagreements and pure flying/hull rows collapse to a
78
+ * single value only when unambiguous; otherwise the unit is omitted.
79
+ */
80
+ export declare function buildBevyIndex(src: BevySources): Map<string, BaseSize>;
81
+ export interface CompositionModel {
82
+ name: string;
83
+ min: number;
84
+ max: number;
85
+ is_leader_model?: boolean;
86
+ }
87
+ export interface UnitInput {
88
+ id: string;
89
+ /** Folded composition model list, in declared order. */
90
+ models: CompositionModel[];
91
+ }
92
+ export interface UnitAssignment {
93
+ /** Representative unit-level base (most-numerous model). Absent if unresolved. */
94
+ unitBase?: BaseSize;
95
+ /** Per-model bases keyed by the model's `name` (only models that resolved). */
96
+ modelBases: Map<string, BaseSize>;
97
+ /** How the unit-level base was sourced. */
98
+ source: "guide" | "bevy" | "none";
99
+ }
100
+ export interface AssignmentReport {
101
+ /** Units with no base from any source. */
102
+ unmatched: string[];
103
+ /** Units whose base came from the bevy fallback (guide omitted them). */
104
+ bevyFallback: string[];
105
+ /** "unitId: modelName" for composition models that resolved no base. */
106
+ unresolvedModels: string[];
107
+ /** Guide rows whose size string failed to parse. */
108
+ guideUnparsed: GuideRow[];
109
+ }
110
+ /**
111
+ * Resolve unit-level and per-model bases for every unit.
112
+ *
113
+ * Per model: guide override → guide default → bevy fallback.
114
+ * Unit-level representative: the most-numerous model's base (tie → larger
115
+ * footprint → earliest declared order), so simple consumers get one stable value
116
+ * while the per-model breakdown stays exact.
117
+ */
118
+ export declare function assignBaseSizes(units: readonly UnitInput[], guide: GuideIndex, bevy: Map<string, BaseSize>): {
119
+ assignments: Map<string, UnitAssignment>;
120
+ report: AssignmentReport;
121
+ };
122
+ //# sourceMappingURL=base-size-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-size-bridge.d.ts","sourceRoot":"","sources":["../../src/converters/base-size-bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Bridge GW base-size data onto units and their per-model compositions.
3
+ *
4
+ * Two sources, in priority order:
5
+ * 1. The GW *Chapter Approved Tournament Companion — Base Size Guide* (committed
6
+ * as numerical-facts rows in `data/base-size-guide.json`). Authoritative for
7
+ * current matched-play units. Multi-model datasheets list each model as a
8
+ * "Unit: ModelLabel" row; the bare "Unit" row is the default for unlisted models.
9
+ * 2. bevy-deploy-helper's per-model table, used only as a fallback for the Forge
10
+ * World / Legends units the tournament guide omits.
11
+ *
12
+ * The match key is the folded datasheet name — 40kdc unit ids are themselves
13
+ * generated from GW names (see `id-generator.ts`), so a direct fold joins exactly.
14
+ * Per-model bases attach to the existing `unit-composition` model entries; the
15
+ * unit-level `base_size_mm` is the representative (most-numerous) model's base.
16
+ *
17
+ * Nothing here touches the filesystem — see `commands/populate-base-sizes.ts`.
18
+ */
19
+ /** Fold a display name to the same kebab key `nameToId` produces, but never throws. */
20
+ export function foldName(s) {
21
+ return s
22
+ .normalize("NFD")
23
+ .replace(/[̀-ͯ]/g, "") // strip diacritics
24
+ .replace(/['‘’]/g, "") // strip apostrophes / curly quotes
25
+ .toLowerCase()
26
+ .replace(/[^a-z0-9]+/g, "-")
27
+ .replace(/^-+|-+$/g, "");
28
+ }
29
+ /**
30
+ * Parse a raw base-size string. Tolerant of curly quotes, `×`, decimals, spacing
31
+ * and the "Oval Base" suffix. Categories the guide gives without authoritative
32
+ * millimetres (`Hull`, `Unique`, `Small Flying Base`) parse to draft entries;
33
+ * `Large Flying Base` additionally gets its well-attested 120×92mm oval.
34
+ */
35
+ export function parseBaseSize(raw) {
36
+ const s = (raw ?? "").normalize("NFKC").replace(/[“”‘’]/g, "").trim();
37
+ if (s === "")
38
+ return { ok: false, reason: "empty" };
39
+ if (/^hull$/i.test(s))
40
+ return { ok: true, base: { shape: "hull", draft: true } };
41
+ if (/^unique$/i.test(s))
42
+ return { ok: true, base: { shape: "unique", draft: true } };
43
+ const flying = s.match(/^(large|small)\s+flying\s+base$/i);
44
+ if (flying) {
45
+ const size = flying[1].toLowerCase() === "large" ? "large" : "small";
46
+ const base = { shape: "flying-base", size, draft: true };
47
+ // The large flying stem ships on a 120×92mm oval; the small one has no standard mm.
48
+ if (size === "large") {
49
+ base.width = 120;
50
+ base.length = 92;
51
+ }
52
+ return { ok: true, base };
53
+ }
54
+ const oval = s.match(/(\d+(?:\.\d+)?)\s*[xX×]\s*(\d+(?:\.\d+)?)\s*mm/);
55
+ if (oval)
56
+ return { ok: true, base: { shape: "oval", width: Number(oval[1]), length: Number(oval[2]) } };
57
+ const round = s.match(/(\d+(?:\.\d+)?)\s*mm/);
58
+ if (round)
59
+ return { ok: true, base: { shape: "round", diameter: Number(round[1]) } };
60
+ return { ok: false, reason: "unparseable" };
61
+ }
62
+ /** Stable key for comparing two parsed bases (used to detect bevy disagreements). */
63
+ function baseKey(b) {
64
+ switch (b.shape) {
65
+ case "round":
66
+ return `R${b.diameter}`;
67
+ case "oval":
68
+ return `O${b.width}x${b.length}`;
69
+ case "flying-base":
70
+ return `F${b.size}`;
71
+ default:
72
+ return b.shape;
73
+ }
74
+ }
75
+ /** Footprint area used only to break representative-base ties; categories sort last. */
76
+ function baseArea(b) {
77
+ if (b.shape === "round" && b.diameter)
78
+ return Math.PI * (b.diameter / 2) ** 2;
79
+ if (b.shape === "oval" && b.width && b.length)
80
+ return (Math.PI * b.width * b.length) / 4;
81
+ return 0;
82
+ }
83
+ /** Build the guide index from committed rows. Splits "A/B" shared rows on "/". */
84
+ export function buildGuideIndex(rows) {
85
+ const byUnit = new Map();
86
+ const unparsed = [];
87
+ for (const row of rows) {
88
+ const parsed = parseBaseSize(row.raw);
89
+ if (!parsed.ok) {
90
+ unparsed.push(row);
91
+ continue;
92
+ }
93
+ for (const parent of row.unit.split("/")) {
94
+ const unitId = foldName(parent);
95
+ if (!unitId)
96
+ continue;
97
+ let entry = byUnit.get(unitId);
98
+ if (!entry) {
99
+ entry = { overrides: new Map() };
100
+ byUnit.set(unitId, entry);
101
+ }
102
+ if (row.model)
103
+ entry.overrides.set(foldName(row.model), parsed.base);
104
+ else
105
+ entry.default = parsed.base;
106
+ }
107
+ }
108
+ return { byUnit, unparsed };
109
+ }
110
+ /**
111
+ * Build a unit-id → base map from bevy, keeping only datasheets whose model rows
112
+ * agree on a single base. Disagreements and pure flying/hull rows collapse to a
113
+ * single value only when unambiguous; otherwise the unit is omitted.
114
+ */
115
+ export function buildBevyIndex(src) {
116
+ const nameById = new Map(src.datasheets.map((d) => [d.id, d.name]));
117
+ const distinctByUnit = new Map();
118
+ for (const m of src.models) {
119
+ const name = nameById.get(m.datasheet_id);
120
+ if (!name)
121
+ continue;
122
+ const unitId = foldName(name);
123
+ if (!unitId)
124
+ continue;
125
+ const parsed = parseBaseSize(m.base_size ?? "");
126
+ if (!parsed.ok)
127
+ continue;
128
+ let seen = distinctByUnit.get(unitId);
129
+ if (!seen) {
130
+ seen = new Map();
131
+ distinctByUnit.set(unitId, seen);
132
+ }
133
+ seen.set(baseKey(parsed.base), parsed.base);
134
+ }
135
+ const index = new Map();
136
+ for (const [unitId, seen] of distinctByUnit) {
137
+ if (seen.size === 1)
138
+ index.set(unitId, [...seen.values()][0]);
139
+ }
140
+ return index;
141
+ }
142
+ /**
143
+ * Resolve unit-level and per-model bases for every unit.
144
+ *
145
+ * Per model: guide override → guide default → bevy fallback.
146
+ * Unit-level representative: the most-numerous model's base (tie → larger
147
+ * footprint → earliest declared order), so simple consumers get one stable value
148
+ * while the per-model breakdown stays exact.
149
+ */
150
+ export function assignBaseSizes(units, guide, bevy) {
151
+ const assignments = new Map();
152
+ const report = {
153
+ unmatched: [],
154
+ bevyFallback: [],
155
+ unresolvedModels: [],
156
+ guideUnparsed: guide.unparsed,
157
+ };
158
+ for (const unit of units) {
159
+ const entry = guide.byUnit.get(unit.id);
160
+ const bevyBase = bevy.get(unit.id);
161
+ const modelBases = new Map();
162
+ // Per-model resolution.
163
+ for (const model of unit.models) {
164
+ const fromGuide = entry?.overrides.get(foldName(model.name)) ?? entry?.default;
165
+ const base = fromGuide ?? bevyBase;
166
+ if (base)
167
+ modelBases.set(model.name, base);
168
+ else
169
+ report.unresolvedModels.push(`${unit.id}: ${model.name}`);
170
+ }
171
+ // Representative unit-level base: most-numerous resolved model.
172
+ let unitBase;
173
+ const resolved = unit.models
174
+ .map((m, i) => ({ m, i, base: modelBases.get(m.name) }))
175
+ .filter((x) => x.base != null);
176
+ if (resolved.length > 0) {
177
+ resolved.sort((a, b) => b.m.max - a.m.max || baseArea(b.base) - baseArea(a.base) || a.i - b.i);
178
+ unitBase = resolved[0].base;
179
+ }
180
+ else {
181
+ // No composition model resolved (e.g. empty model list); fall back to unit-level source.
182
+ unitBase = entry?.default ?? bevyBase;
183
+ }
184
+ let source = "none";
185
+ if (unitBase) {
186
+ const fromGuide = entry && (entry.default || entry.overrides.size > 0);
187
+ // The representative came from the guide unless the guide had nothing for this unit.
188
+ source = fromGuide ? "guide" : "bevy";
189
+ }
190
+ if (!unitBase)
191
+ report.unmatched.push(unit.id);
192
+ else if (source === "bevy")
193
+ report.bevyFallback.push(unit.id);
194
+ assignments.set(unit.id, { unitBase, modelBases, source });
195
+ }
196
+ return { assignments, report };
197
+ }
198
+ //# sourceMappingURL=base-size-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-size-bridge.js","sourceRoot":"","sources":["../../src/converters/base-size-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bridge GW base-size data onto units and their per-model compositions.\n *\n * Two sources, in priority order:\n * 1. The GW *Chapter Approved Tournament Companion — Base Size Guide* (committed\n * as numerical-facts rows in `data/base-size-guide.json`). Authoritative for\n * current matched-play units. Multi-model datasheets list each model as a\n * \"Unit: ModelLabel\" row; the bare \"Unit\" row is the default for unlisted models.\n * 2. bevy-deploy-helper's per-model table, used only as a fallback for the Forge\n * World / Legends units the tournament guide omits.\n *\n * The match key is the folded datasheet name — 40kdc unit ids are themselves\n * generated from GW names (see `id-generator.ts`), so a direct fold joins exactly.\n * Per-model bases attach to the existing `unit-composition` model entries; the\n * unit-level `base_size_mm` is the representative (most-numerous) model's base.\n *\n * Nothing here touches the filesystem — see `commands/populate-base-sizes.ts`.\n */\n\n/** A resolved base size, shaped to the `base-size` schema def. */\nexport interface BaseSize {\n shape: \"round\" | \"oval\" | \"flying-base\" | \"hull\" | \"unique\";\n diameter?: number;\n width?: number;\n length?: number;\n size?: \"small\" | \"large\";\n /** Provisional/guessed (a category without authoritative dims). Omitted when false. */\n draft?: true;\n}\n\n/** Fold a display name to the same kebab key `nameToId` produces, but never throws. */\nexport function foldName(s: string): string {\n return s\n .normalize(\"NFD\")\n .replace(/[̀-ͯ]/g, \"\") // strip diacritics\n .replace(/['‘’]/g, \"\") // strip apostrophes / curly quotes\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\nexport type ParseResult =\n | { ok: true; base: BaseSize }\n | { ok: false; reason: \"empty\" | \"unparseable\" };\n\n/**\n * Parse a raw base-size string. Tolerant of curly quotes, `×`, decimals, spacing\n * and the \"Oval Base\" suffix. Categories the guide gives without authoritative\n * millimetres (`Hull`, `Unique`, `Small Flying Base`) parse to draft entries;\n * `Large Flying Base` additionally gets its well-attested 120×92mm oval.\n */\nexport function parseBaseSize(raw: string): ParseResult {\n const s = (raw ?? \"\").normalize(\"NFKC\").replace(/[“”‘’]/g, \"\").trim();\n if (s === \"\") return { ok: false, reason: \"empty\" };\n\n if (/^hull$/i.test(s)) return { ok: true, base: { shape: \"hull\", draft: true } };\n if (/^unique$/i.test(s)) return { ok: true, base: { shape: \"unique\", draft: true } };\n\n const flying = s.match(/^(large|small)\\s+flying\\s+base$/i);\n if (flying) {\n const size = flying[1].toLowerCase() === \"large\" ? \"large\" : \"small\";\n const base: BaseSize = { shape: \"flying-base\", size, draft: true };\n // The large flying stem ships on a 120×92mm oval; the small one has no standard mm.\n if (size === \"large\") {\n base.width = 120;\n base.length = 92;\n }\n return { ok: true, base };\n }\n\n const oval = s.match(/(\\d+(?:\\.\\d+)?)\\s*[xX×]\\s*(\\d+(?:\\.\\d+)?)\\s*mm/);\n if (oval) return { ok: true, base: { shape: \"oval\", width: Number(oval[1]), length: Number(oval[2]) } };\n\n const round = s.match(/(\\d+(?:\\.\\d+)?)\\s*mm/);\n if (round) return { ok: true, base: { shape: \"round\", diameter: Number(round[1]) } };\n\n return { ok: false, reason: \"unparseable\" };\n}\n\n/** Stable key for comparing two parsed bases (used to detect bevy disagreements). */\nfunction baseKey(b: BaseSize): string {\n switch (b.shape) {\n case \"round\":\n return `R${b.diameter}`;\n case \"oval\":\n return `O${b.width}x${b.length}`;\n case \"flying-base\":\n return `F${b.size}`;\n default:\n return b.shape;\n }\n}\n\n/** Footprint area used only to break representative-base ties; categories sort last. */\nfunction baseArea(b: BaseSize): number {\n if (b.shape === \"round\" && b.diameter) return Math.PI * (b.diameter / 2) ** 2;\n if (b.shape === \"oval\" && b.width && b.length) return (Math.PI * b.width * b.length) / 4;\n return 0;\n}\n\n// ─── Guide index ─────────────────────────────────────────────────────\n\nexport interface GuideRow {\n unit: string;\n model?: string;\n raw: string;\n}\n\nexport interface GuideEntry {\n /** Base from the bare \"Unit\" row, applied to models without an override. */\n default?: BaseSize;\n /** Per-model overrides, keyed by folded model label. */\n overrides: Map<string, BaseSize>;\n}\n\nexport interface GuideIndex {\n byUnit: Map<string, GuideEntry>;\n /** Rows whose size string failed to parse (should be empty for guide data). */\n unparsed: GuideRow[];\n}\n\n/** Build the guide index from committed rows. Splits \"A/B\" shared rows on \"/\". */\nexport function buildGuideIndex(rows: readonly GuideRow[]): GuideIndex {\n const byUnit = new Map<string, GuideEntry>();\n const unparsed: GuideRow[] = [];\n\n for (const row of rows) {\n const parsed = parseBaseSize(row.raw);\n if (!parsed.ok) {\n unparsed.push(row);\n continue;\n }\n for (const parent of row.unit.split(\"/\")) {\n const unitId = foldName(parent);\n if (!unitId) continue;\n let entry = byUnit.get(unitId);\n if (!entry) {\n entry = { overrides: new Map() };\n byUnit.set(unitId, entry);\n }\n if (row.model) entry.overrides.set(foldName(row.model), parsed.base);\n else entry.default = parsed.base;\n }\n }\n return { byUnit, unparsed };\n}\n\n// ─── bevy fallback index ─────────────────────────────────────────────\n\nexport interface BevySources {\n /** bevy Datasheets.json: [{ id, name }]. */\n datasheets: ReadonlyArray<{ id: string; name: string }>;\n /** bevy Datasheets_models.json: [{ datasheet_id, base_size }]. */\n models: ReadonlyArray<{ datasheet_id: string; base_size?: string }>;\n}\n\n/**\n * Build a unit-id → base map from bevy, keeping only datasheets whose model rows\n * agree on a single base. Disagreements and pure flying/hull rows collapse to a\n * single value only when unambiguous; otherwise the unit is omitted.\n */\nexport function buildBevyIndex(src: BevySources): Map<string, BaseSize> {\n const nameById = new Map(src.datasheets.map((d) => [d.id, d.name]));\n const distinctByUnit = new Map<string, Map<string, BaseSize>>();\n\n for (const m of src.models) {\n const name = nameById.get(m.datasheet_id);\n if (!name) continue;\n const unitId = foldName(name);\n if (!unitId) continue;\n const parsed = parseBaseSize(m.base_size ?? \"\");\n if (!parsed.ok) continue;\n let seen = distinctByUnit.get(unitId);\n if (!seen) {\n seen = new Map();\n distinctByUnit.set(unitId, seen);\n }\n seen.set(baseKey(parsed.base), parsed.base);\n }\n\n const index = new Map<string, BaseSize>();\n for (const [unitId, seen] of distinctByUnit) {\n if (seen.size === 1) index.set(unitId, [...seen.values()][0]);\n }\n return index;\n}\n\n// ─── Assignment ──────────────────────────────────────────────────────\n\nexport interface CompositionModel {\n name: string;\n min: number;\n max: number;\n is_leader_model?: boolean;\n}\n\nexport interface UnitInput {\n id: string;\n /** Folded composition model list, in declared order. */\n models: CompositionModel[];\n}\n\nexport interface UnitAssignment {\n /** Representative unit-level base (most-numerous model). Absent if unresolved. */\n unitBase?: BaseSize;\n /** Per-model bases keyed by the model's `name` (only models that resolved). */\n modelBases: Map<string, BaseSize>;\n /** How the unit-level base was sourced. */\n source: \"guide\" | \"bevy\" | \"none\";\n}\n\nexport interface AssignmentReport {\n /** Units with no base from any source. */\n unmatched: string[];\n /** Units whose base came from the bevy fallback (guide omitted them). */\n bevyFallback: string[];\n /** \"unitId: modelName\" for composition models that resolved no base. */\n unresolvedModels: string[];\n /** Guide rows whose size string failed to parse. */\n guideUnparsed: GuideRow[];\n}\n\n/**\n * Resolve unit-level and per-model bases for every unit.\n *\n * Per model: guide override → guide default → bevy fallback.\n * Unit-level representative: the most-numerous model's base (tie → larger\n * footprint → earliest declared order), so simple consumers get one stable value\n * while the per-model breakdown stays exact.\n */\nexport function assignBaseSizes(\n units: readonly UnitInput[],\n guide: GuideIndex,\n bevy: Map<string, BaseSize>,\n): { assignments: Map<string, UnitAssignment>; report: AssignmentReport } {\n const assignments = new Map<string, UnitAssignment>();\n const report: AssignmentReport = {\n unmatched: [],\n bevyFallback: [],\n unresolvedModels: [],\n guideUnparsed: guide.unparsed,\n };\n\n for (const unit of units) {\n const entry = guide.byUnit.get(unit.id);\n const bevyBase = bevy.get(unit.id);\n const modelBases = new Map<string, BaseSize>();\n\n // Per-model resolution.\n for (const model of unit.models) {\n const fromGuide = entry?.overrides.get(foldName(model.name)) ?? entry?.default;\n const base = fromGuide ?? bevyBase;\n if (base) modelBases.set(model.name, base);\n else report.unresolvedModels.push(`${unit.id}: ${model.name}`);\n }\n\n // Representative unit-level base: most-numerous resolved model.\n let unitBase: BaseSize | undefined;\n const resolved = unit.models\n .map((m, i) => ({ m, i, base: modelBases.get(m.name) }))\n .filter((x): x is { m: CompositionModel; i: number; base: BaseSize } => x.base != null);\n if (resolved.length > 0) {\n resolved.sort((a, b) => b.m.max - a.m.max || baseArea(b.base) - baseArea(a.base) || a.i - b.i);\n unitBase = resolved[0].base;\n } else {\n // No composition model resolved (e.g. empty model list); fall back to unit-level source.\n unitBase = entry?.default ?? bevyBase;\n }\n\n let source: UnitAssignment[\"source\"] = \"none\";\n if (unitBase) {\n const fromGuide = entry && (entry.default || entry.overrides.size > 0);\n // The representative came from the guide unless the guide had nothing for this unit.\n source = fromGuide ? \"guide\" : \"bevy\";\n }\n\n if (!unitBase) report.unmatched.push(unit.id);\n else if (source === \"bevy\") report.bevyFallback.push(unit.id);\n\n assignments.set(unit.id, { unitBase, modelBases, source });\n }\n\n return { assignments, report };\n}\n"]}
@@ -0,0 +1,11 @@
1
+ export interface GuideRow {
2
+ /** Left-of-colon datasheet name(s). May be "A/B" for shared rows. */
3
+ unit: string;
4
+ /** Right-of-colon per-model label, when the row is model-specific. */
5
+ model?: string;
6
+ /** Verbatim base-size string (e.g. "32mm", "60 x 35.5mm Oval Base", "Hull"). */
7
+ raw: string;
8
+ }
9
+ /** Parse the layout-mode text of the Base Size Guide into structured rows. */
10
+ export declare function extractGuideRows(layoutText: string): GuideRow[];
11
+ //# sourceMappingURL=base-size-guide-extract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-size-guide-extract.d.ts","sourceRoot":"","sources":["../../src/converters/base-size-guide-extract.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,QAAQ;IACvB,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,GAAG,EAAE,MAAM,CAAC;CACb;AAUD,8EAA8E;AAC9E,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,EAAE,CAmB/D"}