Files
story_builder/scripts/import-tables.js
2026-06-30 09:10:05 +10:00

129 lines
4.3 KiB
JavaScript

const fs = require('fs');
const path = require('path');
const dbApi = require('../db.js');
const { parseMarkdownFile } = require('../lib/markdownTableParser.js');
const TABLES_DIR = path.join(__dirname, '..', 'tables');
const NPC_CORE_COLUMNS = [
'first name', 'last name', 'brief description', 'wants and needs', 'secret or obstacle', 'also carrying',
];
const NPC_ATTRIBUTE_PATTERNS = [
[/\bpronoun/i, 'npc_pronouns'],
[/\bage\b/i, 'npc_age'],
[/\bintelligence\b/i, 'npc_intelligence'],
[/^hair$/i, 'npc_hair'],
[/^build$/i, 'npc_build'],
[/^race$/i, 'npc_race'],
];
const LAYER1_LINKS = [
[/weather turns/i, 'Layer 2C: Weather Turns (d20)'],
[/encounter a person/i, 'Layer 2A: Person Encountered (d20)'],
[/encounter a group/i, 'Layer 2B: Group Encountered (d20)'],
[/find something/i, 'Layer 2D: Find Something (d100)'],
[/natural hazard/i, 'Layer 2E: Natural Hazard (d20)'],
[/signs of recent violence/i, 'Layer 2F: Signs of Recent Violence (d20)'],
[/pursuit or being followed/i, 'Layer 2G: Pursuit \/ Being Followed (d20)'],
[/physical hardship/i, 'Layer 2H: Physical Hardship (d20)'],
[/something uncanny/i, 'Layer 2I: Something Uncanny (d20)'],
];
function isNpcCoreTable(columns) {
const norm = columns.map((c) => c.toLowerCase().trim());
return NPC_CORE_COLUMNS.every((req) => norm.includes(req));
}
function detectNpcRole(headingText, columns, insideNpcSection) {
if (isNpcCoreTable(columns)) return 'npc_core';
if (!insideNpcSection) return null;
const trimmed = headingText.trim();
for (const [pattern, role] of NPC_ATTRIBUTE_PATTERNS) {
if (pattern.test(trimmed)) return role;
}
return null;
}
const stats = { files: 0, nodes: 0, tables: 0, rows: 0, links: 0 };
function insertNode(node, sourceFile, parentId, sortOrder, insideNpcSection) {
const treeNodeId = dbApi.tables.insertTreeNode({
source_file: sourceFile,
parent_id: parentId,
heading_text: node.headingText,
heading_level: node.level,
sort_order: sortOrder,
});
stats.nodes++;
const childInsideNpc = insideNpcSection || /random npc generator/i.test(node.headingText || '');
if (node.table) {
const npcRole = detectNpcRole(node.headingText, node.table.columns, childInsideNpc);
const tableId = dbApi.tables.insertTable({
tree_node_id: treeNodeId,
dice_notation: node.table.diceNotation,
table_type: node.table.tableType,
npc_role: npcRole,
});
stats.tables++;
dbApi.tables.insertColumns(tableId, node.table.columns);
const rowIds = dbApi.tables.insertRows(tableId, node.table.rows);
stats.rows += rowIds.length;
}
node.children.forEach((child, idx) => insertNode(child, sourceFile, treeNodeId, idx, childInsideNpc));
}
function importFile(filePath) {
const sourceFile = path.basename(filePath);
const roots = parseMarkdownFile(filePath);
roots.forEach((node, idx) => insertNode(node, sourceFile, null, idx, false));
stats.files++;
console.log(`Imported ${sourceFile}`);
}
function resolveLayer1Links() {
const sourceFile = 'norse_encounter_tables.md';
const layer1Table = dbApi.tables.findTableByHeading(sourceFile, 'Layer 1: What Happens? (d20)');
if (!layer1Table) {
console.warn('Could not find Layer 1 table for link resolution');
return;
}
const rows = dbApi.tables.getRowsForTable(layer1Table.id);
for (const row of rows) {
const text = row.cells[0] || '';
const match = LAYER1_LINKS.find(([pattern]) => pattern.test(text));
if (!match) continue;
const [, targetHeading] = match;
const targetTable = dbApi.tables.findTableByHeading(sourceFile, targetHeading);
if (!targetTable) {
console.warn(`Layer 1 link target not found: "${targetHeading}" (row: "${text}")`);
continue;
}
dbApi.tables.insertLink({ table_id: layer1Table.id, row_id: row.id, target_table_id: targetTable.id });
stats.links++;
}
}
function main() {
dbApi.tables.clearImported();
const files = fs.readdirSync(TABLES_DIR).filter((f) => f.endsWith('.md'));
for (const file of files) {
importFile(path.join(TABLES_DIR, file));
}
resolveLayer1Links();
console.log('---');
console.log(`Files: ${stats.files}`);
console.log(`Tree nodes: ${stats.nodes}`);
console.log(`Tables: ${stats.tables}`);
console.log(`Rows: ${stats.rows}`);
console.log(`Layer links: ${stats.links}`);
}
main();