Science Fiction Story Planner Generator
Who is the protagonist, what do they want, and what is the conflict?
Who holds power? (e.g., Corporate Oligarchy, Benevolent AI, Religious Dictatorship)
The defining technology of your world (e.g., FTL drive, bio-engineered immortality, telepathic network).
What is the trade-off or risk of this technology?
How does the technology create or solve the main problem?
The event that starts the story and forces the protagonist into action.
What is lost if the protagonist fails? (Personal stakes vs. Global stakes)
The moment the protagonist confronts the main conflict.
Review your complete story plan. The plan is structured for easy reference and future development.
Click "Next" or "Previous" to refresh this preview.
Logline: ${escapeHTML(data.logline)}
Sub-Genre: ${escapeHTML(data.genre)}
Protagonist: ${escapeHTML(data.mainCharacter)}
2. Worldbuilding
Primary Setting: ${escapeHTML(data.setting)}
Political Structure: ${escapeHTML(data.gov)}
Daily Life: ${escapeHTML(data.dailyLife)}
Unique Rules: ${escapeHTML(data.uniqueRules)}
3. Technology & Magic
Core Innovation: ${escapeHTML(data.coreTech)}
Limitations/Costs: ${escapeHTML(data.techLimitations)}
Role in Conflict: ${escapeHTML(data.techRole)}
4. Plot & Conflict
Inciting Incident: ${escapeHTML(data.incitingIncident)}
Stakes: ${escapeHTML(data.stakes)}
Climax: ${escapeHTML(data.climax)}
`; }; const getTxtContent = () => { const data = getPlanData(); let content = `SCIENCE FICTION STORY PLAN: ${data.title.toUpperCase()}\n`; content += "========================================================\n\n"; content += "1. CORE PREMISE\n"; content += "--------------------\n"; content += `Logline: ${data.logline}\n`; content += `Sub-Genre: ${data.genre}\n`; content += `Protagonist: ${data.mainCharacter}\n\n`; content += "2. WORLD BUILDING\n"; content += "--------------------\n"; content += `Primary Setting: ${data.setting}\n\n`; content += `Political Structure: ${data.gov}\n\n`; content += `Daily Life: ${data.dailyLife}\n\n`; content += `Unique Laws/Rules: ${data.uniqueRules}\n\n`; content += "3. TECHNOLOGY & MAGIC\n"; content += "--------------------\n"; content += `Core Innovation: ${data.coreTech}\n\n`; content += `Limitations/Costs: ${data.techLimitations}\n\n`; content += `Role in Conflict: ${data.techRole}\n\n`; content += "4. PLOT & CONFLICT\n"; content += "--------------------\n"; content += `Inciting Incident: ${data.incitingIncident}\n\n`; content += `Stakes: ${data.stakes}\n\n`; content += `Climax: ${data.climax}\n`; return content; }; const downloadTxt = () => { const txtContent = getTxtContent(); const data = getPlanData(); const blob = new Blob([txtContent], { type: 'text/plain;charset=utf-8' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = `${data.title.replace(/ /g, '_') || 'scifi_plan'}.txt`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(a.href); }; const downloadPDF = () => { if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') { alert('Error: jsPDF library not loaded.'); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF('p', 'mm', 'a4'); const data = getPlanData(); const margin = 20; const pageWidth = doc.internal.pageSize.getWidth(); const usableWidth = pageWidth - (margin * 2); let yPos = 20; const addTitle = (text, size, style, color = [44, 62, 80], align = 'left') => { doc.setFontSize(size); doc.setFont(undefined, style); doc.setTextColor(color[0], color[1], color[2]); doc.text(text, pageWidth / 2, yPos, { align: 'center' }); yPos += size / 2 + 5; }; const addSectionTitle = (text) => { yPos += 5; doc.setFontSize(14); doc.setFont(undefined, 'bold'); doc.setTextColor(139, 92, 246); /* Violet */ doc.text(text, margin, yPos); doc.setDrawColor(224, 224, 224); doc.line(margin, yPos + 1, pageWidth - margin, yPos + 1); yPos += 8; }; const addContent = (label, text) => { doc.setFontSize(11); doc.setFont(undefined, 'bold'); doc.setTextColor(44, 62, 80); doc.text(label, margin, yPos); doc.setFont(undefined, 'normal'); doc.setTextColor(52, 73, 94); // Indent content for readability const splitText = doc.splitTextToSize(text, usableWidth - 5); // Check for page break if (yPos + (splitText.length * 6) > 270) { doc.addPage(); yPos = 20; } doc.text(splitText, margin + 5, yPos + 6); yPos += (splitText.length * 6) + 10; }; // --- Build PDF Document --- addTitle("Science Fiction Story Planner", 18, 'bold'); addTitle(data.title, 14, 'normal', [44, 62, 80]); yPos += 5; // 1. Core Premise addSectionTitle("1. Core Premise"); addContent("Logline:", data.logline); addContent("Sub-Genre:", data.genre); addContent("Protagonist:", data.mainCharacter); // 2. Worldbuilding addSectionTitle("2. Worldbuilding"); addContent("Primary Setting:", data.setting); addContent("Political Structure:", data.gov); addContent("Daily Life:", data.dailyLife); addContent("Unique Rules:", data.uniqueRules); // 3. Technology & Magic addSectionTitle("3. Technology & Magic"); addContent("Core Innovation:", data.coreTech); addContent("Limitations/Costs:", data.techLimitations); addContent("Role in Conflict:", data.techRole); // 4. Plot & Conflict addSectionTitle("4. Plot & Conflict"); addContent("Inciting Incident:", data.incitingIncident); addContent("Stakes:", data.stakes); addContent("Climax:", data.climax); doc.save(`scifi_plan_${data.title.replace(/ /g, '_') || 'plan'}.pdf`); }; // --- Event Listeners --- // Tab Buttons tabButtons.forEach((btn, index) => { btn.addEventListener('click', () => showTab(index + 1)); }); // Next/Prev Navigation nextBtn.addEventListener('click', () => showTab(currentTab + 1)); prevBtn.addEventListener('click', () => showTab(currentTab - 1)); // Tab 5 Actions downloadPdfBtn.addEventListener('click', downloadPDF); downloadTxtBtn.addEventListener('click', downloadTxt); // --- Initialization --- showTab(1); // Set initial state });