Visual Novel Planner Generator
Structure narrative paths, manage assets, and track character requirements.
Project & Story Overview
Character Definition (Main)
Asset Requirements (CGs, Backgrounds, Music)
Narrative Path Logic
Visual Novel Project Brief
Genre: | Protagonist:
I. Core Premise & Scope
Total Character Routes Planned:
II. Narrative Paths & Endings
No paths defined.
III. Assets & Production Requirements
No assets defined.
No paths defined.
'; return; } // Group paths by type for cleaner display const groupedPaths = paths.reduce((acc, p) => { if (!acc[p.type]) acc[p.type] = []; acc[p.type].push(p); return acc; }, {}); Object.keys(groupedPaths).forEach(type => { list.innerHTML += `${type}:
`;
groupedPaths[type].forEach(p => {
const item = document.createElement('div');
item.className = 'vnpg-list-item bg-white';
item.innerHTML = `
${escapeHtml(p.name)}
`;
list.appendChild(item);
});
});
}
// --- Preview Generation ---
window.vnpgUpdatePreview = function() {
// Metadata
const title = document.getElementById('vnpg-title').value || "Project Title";
const genre = document.getElementById('vnpg-genre').value || "N/A";
const protagonist = document.getElementById('vnpg-protagonist').value || "Protagonist";
const logline = document.getElementById('vnpg-logline').value || "Logline missing.";
const routeCount = document.getElementById('vnpg-route-count').value || "N/A";
// Update Display
document.getElementById('disp-title').textContent = title;
document.getElementById('disp-genre').textContent = genre;
document.getElementById('disp-protagonist').textContent = protagonist;
document.getElementById('disp-logline').textContent = logline;
document.getElementById('disp-route-count').textContent = routeCount;
// Path Summary
const pathSummaryDiv = document.getElementById('disp-path-summary');
const groupedPaths = paths.reduce((acc, p) => {
if (!acc[p.type]) acc[p.type] = [];
acc[p.type].push(p);
return acc;
}, {});
if (paths.length === 0) {
pathSummaryDiv.innerHTML = 'No paths defined.
'; } else { let pathHtml = ''; Object.keys(groupedPaths).forEach(type => { pathHtml += `${type} (${groupedPaths[type].length}):
`; groupedPaths[type].forEach(p => { pathHtml += `- ${escapeHtml(p.name)}
`;
});
});
pathSummaryDiv.innerHTML = pathHtml;
}
// Asset Summary
const assetSummaryDiv = document.getElementById('disp-asset-summary');
if (assets.length === 0) {
assetSummaryDiv.innerHTML = 'No assets defined.
'; } else { const groupedAssets = assets.reduce((acc, a) => { if (!acc[a.type]) acc[a.type] = []; acc[a.type].push(a); return acc; }, {}); let assetHtml = '- ';
Object.keys(groupedAssets).forEach(type => {
assetHtml += `
- ${type} (${groupedAssets[type].length} items): `; groupedAssets[type].forEach(a => { assetHtml += `
- ${escapeHtml(a.desc)}
`;
});
});
assetHtml += '